summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--CMakeLists.txt22
-rw-r--r--src/CMakeLists.txt53
-rw-r--r--src/addbookmarkdialog.ui98
-rw-r--r--src/autosaver.cpp90
-rw-r--r--src/autosaver.h72
-rw-r--r--src/bookmarks.cpp983
-rw-r--r--src/bookmarks.h306
-rw-r--r--src/bookmarks.ui106
-rw-r--r--src/browser.icnsbin0 -> 57624 bytes
-rw-r--r--src/browser.icobin0 -> 287934 bytes
-rw-r--r--src/browser.pro87
-rw-r--r--src/browser.rc2
-rw-r--r--src/browserapplication.cpp439
-rw-r--r--src/browserapplication.h115
-rw-r--r--src/browsermainwindow.cpp969
-rw-r--r--src/browsermainwindow.h164
-rw-r--r--src/chasewidget.cpp138
-rw-r--r--src/chasewidget.h81
-rw-r--r--src/cookiejar.cpp728
-rw-r--r--src/cookiejar.h200
-rw-r--r--src/cookies.ui106
-rw-r--r--src/cookiesexceptions.ui184
-rw-r--r--src/data/addtab.pngbin0 -> 469 bytes
-rw-r--r--src/data/browser.svg1598
-rw-r--r--src/data/closetab.pngbin0 -> 516 bytes
-rw-r--r--src/data/data.qrc11
-rw-r--r--src/data/defaultbookmarks.xbel40
-rw-r--r--src/data/defaulticon.pngbin0 -> 1473 bytes
-rw-r--r--src/data/history.pngbin0 -> 1527 bytes
-rw-r--r--src/data/loading.gifbin0 -> 847 bytes
-rw-r--r--src/downloaditem.ui134
-rw-r--r--src/downloadmanager.cpp575
-rw-r--r--src/downloadmanager.h158
-rw-r--r--src/downloads.ui83
-rw-r--r--src/edittableview.cpp73
-rw-r--r--src/edittableview.h57
-rw-r--r--src/edittreeview.cpp73
-rw-r--r--src/edittreeview.h57
-rw-r--r--src/history.cpp1277
-rw-r--r--src/history.h346
-rw-r--r--src/history.ui106
-rw-r--r--src/htmls/htmls.qrc5
-rwxr-xr-xsrc/htmls/notfound.html63
-rw-r--r--src/main.cpp57
-rw-r--r--src/modelmenu.cpp222
-rw-r--r--src/modelmenu.h101
-rw-r--r--src/networkaccessmanager.cpp154
-rw-r--r--src/networkaccessmanager.h61
-rw-r--r--src/passworddialog.ui111
-rw-r--r--src/proxy.ui104
-rw-r--r--src/searchlineedit.cpp233
-rw-r--r--src/searchlineedit.h99
-rw-r--r--src/settings.cpp320
-rw-r--r--src/settings.h70
-rw-r--r--src/settings.ui614
-rw-r--r--src/squeezelabel.cpp56
-rw-r--r--src/squeezelabel.h56
-rw-r--r--src/tabwidget.cpp859
-rw-r--r--src/tabwidget.h222
-rw-r--r--src/toolbarsearch.cpp156
-rw-r--r--src/toolbarsearch.h80
-rw-r--r--src/urllineedit.cpp327
-rw-r--r--src/urllineedit.h109
-rw-r--r--src/webview.cpp299
-rw-r--r--src/webview.h115
-rw-r--r--src/xbel.cpp315
-rw-r--r--src/xbel.h109
68 files changed, 14449 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..187011aa
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Andrea Diamantini adjam7@gmail.com
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..78d320d6
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Andrea Diamantini - adjam7 at gmail dot com
+# rekonq project
+
+PROJECT( rekonq )
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0)
+
+SET( QT_MIN_VERSION 4.4.0)
+FIND_PACKAGE( KDE4 REQUIRED )
+
+INCLUDE(KDE4Defaults)
+INCLUDE(MacroLibrary)
+
+INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${KDE4_INCLUDES}
+ ${QT4_INCLUDES}
+ ${QT_QTNETWORK_INCLUDE_DIR}
+ ${QT_QTWEBKIT_INCLUDE_DIR}
+)
+
+ADD_SUBDIRECTORY( src )
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..26283c2e
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,53 @@
+SET( rekonq_SRCS
+ autosaver.cpp
+ bookmarks.cpp
+ browserapplication.cpp
+ browsermainwindow.cpp
+ chasewidget.cpp
+ cookiejar.cpp
+ downloadmanager.cpp
+ edittableview.cpp
+ edittreeview.cpp
+ history.cpp
+ modelmenu.cpp
+ networkaccessmanager.cpp
+ searchlineedit.cpp
+ settings.cpp
+ squeezelabel.cpp
+ tabwidget.cpp
+ toolbarsearch.cpp
+ urllineedit.cpp
+ webview.cpp
+ xbel.cpp
+ main.cpp
+)
+
+KDE4_ADD_UI_FILES( rekonq_SRCS
+ addbookmarkdialog.ui
+ bookmarks.ui
+ cookies.ui
+ cookiesexceptions.ui
+ downloaditem.ui
+ downloads.ui
+ history.ui
+ passworddialog.ui
+ proxy.ui
+ settings.ui
+ )
+
+# include(${QT_USE_FILE})
+
+ADD_DEFINITIONS( ${KDE4_DEFINITIONS} )
+
+KDE4_ADD_EXECUTABLE( rekonq ${rekonq_SRCS} )
+
+TARGET_LINK_LIBRARIES( rekonq
+ ${QT_LIBRARIES}
+ ${QT_QTNETWORK_LIBRARY}
+ ${QT_QTWEBKIT_LIBRARY}
+ ${KDE4_KDEUI_LIBS}
+ ${QT_QTUITOOLS_LIBRARY}
+)
+
+INSTALL( TARGETS rekonq ${INSTALL_TARGETS_DEFAULT_ARGS} )
+# install(FILES kekonq.rc DESTINATION ${DATA_INSTALL_DIR}/rekonq)
diff --git a/src/addbookmarkdialog.ui b/src/addbookmarkdialog.ui
new file mode 100644
index 00000000..3460d7bb
--- /dev/null
+++ b/src/addbookmarkdialog.ui
@@ -0,0 +1,98 @@
+<ui version="4.0" >
+ <class>AddBookmarkDialog</class>
+ <widget class="QDialog" name="AddBookmarkDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>240</width>
+ <height>168</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Add Bookmark</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Type a name for the bookmark, and choose where to keep it.</string>
+ </property>
+ <property name="textFormat" >
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="name" />
+ </item>
+ <item>
+ <widget class="QComboBox" name="location" />
+ </item>
+ <item>
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>2</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ <property name="centerButtons" >
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AddBookmarkDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AddBookmarkDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/autosaver.cpp b/src/autosaver.cpp
new file mode 100644
index 00000000..f8d088dd
--- /dev/null
+++ b/src/autosaver.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "autosaver.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMetaObject>
+#include <QtDebug>
+
+#define AUTOSAVE_IN 1000 * 3 // seconds
+#define MAXWAIT 1000 * 15 // seconds
+
+AutoSaver::AutoSaver(QObject *parent) : QObject(parent)
+{
+ Q_ASSERT(parent);
+}
+
+AutoSaver::~AutoSaver()
+{
+ if (m_timer.isActive())
+ qWarning() << "AutoSaver: still active when destroyed, changes not saved.";
+}
+
+void AutoSaver::changeOccurred()
+{
+ if (m_firstChange.isNull())
+ m_firstChange.start();
+
+ if (m_firstChange.elapsed() > MAXWAIT) {
+ saveIfNeccessary();
+ } else {
+ m_timer.start(AUTOSAVE_IN, this);
+ }
+}
+
+void AutoSaver::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_timer.timerId()) {
+ saveIfNeccessary();
+ } else {
+ QObject::timerEvent(event);
+ }
+}
+
+void AutoSaver::saveIfNeccessary()
+{
+ if (!m_timer.isActive())
+ return;
+ m_timer.stop();
+ m_firstChange = QTime();
+ if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) {
+ qWarning() << "AutoSaver: error invoking slot save() on parent";
+ }
+}
+
diff --git a/src/autosaver.h b/src/autosaver.h
new file mode 100644
index 00000000..162b3b6c
--- /dev/null
+++ b/src/autosaver.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef AUTOSAVER_H
+#define AUTOSAVER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QBasicTimer>
+#include <QtCore/QTime>
+
+/*
+ This class will call the save() slot on the parent object when the parent changes.
+ It will wait several seconds after changed() to combining multiple changes and
+ prevent continuous writing to disk.
+ */
+class AutoSaver : public QObject {
+
+Q_OBJECT
+
+public:
+ AutoSaver(QObject *parent);
+ ~AutoSaver();
+ void saveIfNeccessary();
+
+public slots:
+ void changeOccurred();
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private:
+ QBasicTimer m_timer;
+ QTime m_firstChange;
+
+};
+
+#endif // AUTOSAVER_H
+
diff --git a/src/bookmarks.cpp b/src/bookmarks.cpp
new file mode 100644
index 00000000..9b47747d
--- /dev/null
+++ b/src/bookmarks.cpp
@@ -0,0 +1,983 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "bookmarks.h"
+
+#include "autosaver.h"
+#include "browserapplication.h"
+#include "history.h"
+#include "xbel.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QFile>
+#include <QtCore/QMimeData>
+
+#include <QtGui/QDesktopServices>
+#include <QtGui/QDragEnterEvent>
+#include <QtGui/QFileDialog>
+#include <QtGui/QHeaderView>
+#include <QtGui/QIcon>
+#include <QtGui/QMessageBox>
+#include <QtGui/QToolButton>
+
+#include <QtWebKit/QWebSettings>
+
+#include <QtCore/QDebug>
+
+#define BOOKMARKBAR "Bookmarks Bar"
+#define BOOKMARKMENU "Bookmarks Menu"
+
+BookmarksManager::BookmarksManager(QObject *parent)
+ : QObject(parent)
+ , m_loaded(false)
+ , m_saveTimer(new AutoSaver(this))
+ , m_bookmarkRootNode(0)
+ , m_bookmarkModel(0)
+{
+ connect(this, SIGNAL(entryAdded(BookmarkNode *)),
+ m_saveTimer, SLOT(changeOccurred()));
+ connect(this, SIGNAL(entryRemoved(BookmarkNode *, int, BookmarkNode *)),
+ m_saveTimer, SLOT(changeOccurred()));
+ connect(this, SIGNAL(entryChanged(BookmarkNode *)),
+ m_saveTimer, SLOT(changeOccurred()));
+}
+
+BookmarksManager::~BookmarksManager()
+{
+ m_saveTimer->saveIfNeccessary();
+}
+
+void BookmarksManager::changeExpanded()
+{
+ m_saveTimer->changeOccurred();
+}
+
+void BookmarksManager::load()
+{
+ if (m_loaded)
+ return;
+ m_loaded = true;
+
+ QString dir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
+ if (!QFile::exists(bookmarkFile))
+ bookmarkFile = QLatin1String(":defaultbookmarks.xbel");
+
+ XbelReader reader;
+ m_bookmarkRootNode = reader.read(bookmarkFile);
+ if (reader.error() != QXmlStreamReader::NoError) {
+ QMessageBox::warning(0, QLatin1String("Loading Bookmark"),
+ tr("Error when loading bookmarks on line %1, column %2:\n"
+ "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
+ }
+
+ BookmarkNode *toolbar = 0;
+ BookmarkNode *menu = 0;
+ QList<BookmarkNode*> others;
+ for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
+ BookmarkNode *node = m_bookmarkRootNode->children().at(i);
+ if (node->type() == BookmarkNode::Folder) {
+ // Automatically convert
+ if (node->title == tr("Toolbar Bookmarks") && !toolbar) {
+ node->title = tr(BOOKMARKBAR);
+ }
+ if (node->title == tr(BOOKMARKBAR) && !toolbar) {
+ toolbar = node;
+ }
+
+ // Automatically convert
+ if (node->title == tr("Menu") && !menu) {
+ node->title = tr(BOOKMARKMENU);
+ }
+ if (node->title == tr(BOOKMARKMENU) && !menu) {
+ menu = node;
+ }
+ } else {
+ others.append(node);
+ }
+ m_bookmarkRootNode->remove(node);
+ }
+ Q_ASSERT(m_bookmarkRootNode->children().count() == 0);
+ if (!toolbar) {
+ toolbar = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode);
+ toolbar->title = tr(BOOKMARKBAR);
+ } else {
+ m_bookmarkRootNode->add(toolbar);
+ }
+
+ if (!menu) {
+ menu = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode);
+ menu->title = tr(BOOKMARKMENU);
+ } else {
+ m_bookmarkRootNode->add(menu);
+ }
+
+ for (int i = 0; i < others.count(); ++i)
+ menu->add(others.at(i));
+}
+
+void BookmarksManager::save() const
+{
+ if (!m_loaded)
+ return;
+
+ XbelWriter writer;
+ QString dir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
+ if (!writer.write(bookmarkFile, m_bookmarkRootNode))
+ qWarning() << "BookmarkManager: error saving to" << bookmarkFile;
+}
+
+void BookmarksManager::addBookmark(BookmarkNode *parent, BookmarkNode *node, int row)
+{
+ if (!m_loaded)
+ return;
+ Q_ASSERT(parent);
+ InsertBookmarksCommand *command = new InsertBookmarksCommand(this, parent, node, row);
+ m_commands.push(command);
+}
+
+void BookmarksManager::removeBookmark(BookmarkNode *node)
+{
+ if (!m_loaded)
+ return;
+
+ Q_ASSERT(node);
+ BookmarkNode *parent = node->parent();
+ int row = parent->children().indexOf(node);
+ RemoveBookmarksCommand *command = new RemoveBookmarksCommand(this, parent, row);
+ m_commands.push(command);
+}
+
+void BookmarksManager::setTitle(BookmarkNode *node, const QString &newTitle)
+{
+ if (!m_loaded)
+ return;
+
+ Q_ASSERT(node);
+ ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newTitle, true);
+ m_commands.push(command);
+}
+
+void BookmarksManager::setUrl(BookmarkNode *node, const QString &newUrl)
+{
+ if (!m_loaded)
+ return;
+
+ Q_ASSERT(node);
+ ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newUrl, false);
+ m_commands.push(command);
+}
+
+BookmarkNode *BookmarksManager::bookmarks()
+{
+ if (!m_loaded)
+ load();
+ return m_bookmarkRootNode;
+}
+
+BookmarkNode *BookmarksManager::menu()
+{
+ if (!m_loaded)
+ load();
+
+ for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
+ BookmarkNode *node = m_bookmarkRootNode->children().at(i);
+ if (node->title == tr(BOOKMARKMENU))
+ return node;
+ }
+ Q_ASSERT(false);
+ return 0;
+}
+
+BookmarkNode *BookmarksManager::toolbar()
+{
+ if (!m_loaded)
+ load();
+
+ for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
+ BookmarkNode *node = m_bookmarkRootNode->children().at(i);
+ if (node->title == tr(BOOKMARKBAR))
+ return node;
+ }
+ Q_ASSERT(false);
+ return 0;
+}
+
+BookmarksModel *BookmarksManager::bookmarksModel()
+{
+ if (!m_bookmarkModel)
+ m_bookmarkModel = new BookmarksModel(this, this);
+ return m_bookmarkModel;
+}
+
+void BookmarksManager::importBookmarks()
+{
+ QString fileName = QFileDialog::getOpenFileName(0, tr("Open File"),
+ QString(),
+ tr("XBEL (*.xbel *.xml)"));
+ if (fileName.isEmpty())
+ return;
+
+ XbelReader reader;
+ BookmarkNode *importRootNode = reader.read(fileName);
+ if (reader.error() != QXmlStreamReader::NoError) {
+ QMessageBox::warning(0, QLatin1String("Loading Bookmark"),
+ tr("Error when loading bookmarks on line %1, column %2:\n"
+ "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
+ }
+
+ importRootNode->setType(BookmarkNode::Folder);
+ importRootNode->title = (tr("Imported %1").arg(QDate::currentDate().toString(Qt::SystemLocaleShortDate)));
+ addBookmark(menu(), importRootNode);
+}
+
+void BookmarksManager::exportBookmarks()
+{
+ QString fileName = QFileDialog::getSaveFileName(0, tr("Save File"),
+ tr("%1 Bookmarks.xbel").arg(QCoreApplication::applicationName()),
+ tr("XBEL (*.xbel *.xml)"));
+ if (fileName.isEmpty())
+ return;
+
+ XbelWriter writer;
+ if (!writer.write(fileName, m_bookmarkRootNode))
+ QMessageBox::critical(0, tr("Export error"), tr("error saving bookmarks"));
+}
+
+RemoveBookmarksCommand::RemoveBookmarksCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row)
+ : QUndoCommand(BookmarksManager::tr("Remove Bookmark"))
+ , m_row(row)
+ , m_bookmarkManagaer(m_bookmarkManagaer)
+ , m_node(parent->children().value(row))
+ , m_parent(parent)
+ , m_done(false)
+{
+}
+
+RemoveBookmarksCommand::~RemoveBookmarksCommand()
+{
+ if (m_done && !m_node->parent()) {
+ delete m_node;
+ }
+}
+
+void RemoveBookmarksCommand::undo()
+{
+ m_parent->add(m_node, m_row);
+ emit m_bookmarkManagaer->entryAdded(m_node);
+ m_done = false;
+}
+
+void RemoveBookmarksCommand::redo()
+{
+ m_parent->remove(m_node);
+ emit m_bookmarkManagaer->entryRemoved(m_parent, m_row, m_node);
+ m_done = true;
+}
+
+InsertBookmarksCommand::InsertBookmarksCommand(BookmarksManager *m_bookmarkManagaer,
+ BookmarkNode *parent, BookmarkNode *node, int row)
+ : RemoveBookmarksCommand(m_bookmarkManagaer, parent, row)
+{
+ setText(BookmarksManager::tr("Insert Bookmark"));
+ m_node = node;
+}
+
+ChangeBookmarkCommand::ChangeBookmarkCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *node,
+ const QString &newValue, bool title)
+ : QUndoCommand()
+ , m_bookmarkManagaer(m_bookmarkManagaer)
+ , m_title(title)
+ , m_newValue(newValue)
+ , m_node(node)
+{
+ if (m_title) {
+ m_oldValue = m_node->title;
+ setText(BookmarksManager::tr("Name Change"));
+ } else {
+ m_oldValue = m_node->url;
+ setText(BookmarksManager::tr("Address Change"));
+ }
+}
+
+void ChangeBookmarkCommand::undo()
+{
+ if (m_title)
+ m_node->title = m_oldValue;
+ else
+ m_node->url = m_oldValue;
+ emit m_bookmarkManagaer->entryChanged(m_node);
+}
+
+void ChangeBookmarkCommand::redo()
+{
+ if (m_title)
+ m_node->title = m_newValue;
+ else
+ m_node->url = m_newValue;
+ emit m_bookmarkManagaer->entryChanged(m_node);
+}
+
+BookmarksModel::BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent)
+ : QAbstractItemModel(parent)
+ , m_endMacro(false)
+ , m_bookmarksManager(bookmarkManager)
+{
+ connect(bookmarkManager, SIGNAL(entryAdded(BookmarkNode *)),
+ this, SLOT(entryAdded(BookmarkNode *)));
+ connect(bookmarkManager, SIGNAL(entryRemoved(BookmarkNode *, int, BookmarkNode *)),
+ this, SLOT(entryRemoved(BookmarkNode *, int, BookmarkNode *)));
+ connect(bookmarkManager, SIGNAL(entryChanged(BookmarkNode *)),
+ this, SLOT(entryChanged(BookmarkNode *)));
+}
+
+QModelIndex BookmarksModel::index(BookmarkNode *node) const
+{
+ BookmarkNode *parent = node->parent();
+ if (!parent)
+ return QModelIndex();
+ return createIndex(parent->children().indexOf(node), 0, node);
+}
+
+void BookmarksModel::entryAdded(BookmarkNode *item)
+{
+ Q_ASSERT(item && item->parent());
+ int row = item->parent()->children().indexOf(item);
+ BookmarkNode *parent = item->parent();
+ // item was already added so remove beore beginInsertRows is called
+ parent->remove(item);
+ beginInsertRows(index(parent), row, row);
+ parent->add(item, row);
+ endInsertRows();
+}
+
+void BookmarksModel::entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item)
+{
+ // item was already removed, re-add so beginRemoveRows works
+ parent->add(item, row);
+ beginRemoveRows(index(parent), row, row);
+ parent->remove(item);
+ endRemoveRows();
+}
+
+void BookmarksModel::entryChanged(BookmarkNode *item)
+{
+ QModelIndex idx = index(item);
+ emit dataChanged(idx, idx);
+}
+
+bool BookmarksModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || count <= 0 || row + count > rowCount(parent))
+ return false;
+
+ BookmarkNode *bookmarkNode = node(parent);
+ for (int i = row + count - 1; i >= row; --i) {
+ BookmarkNode *node = bookmarkNode->children().at(i);
+ if (node == m_bookmarksManager->menu()
+ || node == m_bookmarksManager->toolbar())
+ continue;
+
+ m_bookmarksManager->removeBookmark(node);
+ }
+ if (m_endMacro) {
+ m_bookmarksManager->undoRedoStack()->endMacro();
+ m_endMacro = false;
+ }
+ return true;
+}
+
+QVariant BookmarksModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0: return tr("Title");
+ case 1: return tr("Address");
+ }
+ }
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+QVariant BookmarksModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.model() != this)
+ return QVariant();
+
+ const BookmarkNode *bookmarkNode = node(index);
+ switch (role) {
+ case Qt::EditRole:
+ case Qt::DisplayRole:
+ if (bookmarkNode->type() == BookmarkNode::Separator) {
+ switch (index.column()) {
+ case 0: return QString(50, 0xB7);
+ case 1: return QString();
+ }
+ }
+
+ switch (index.column()) {
+ case 0: return bookmarkNode->title;
+ case 1: return bookmarkNode->url;
+ }
+ break;
+ case BookmarksModel::UrlRole:
+ return QUrl(bookmarkNode->url);
+ break;
+ case BookmarksModel::UrlStringRole:
+ return bookmarkNode->url;
+ break;
+ case BookmarksModel::TypeRole:
+ return bookmarkNode->type();
+ break;
+ case BookmarksModel::SeparatorRole:
+ return (bookmarkNode->type() == BookmarkNode::Separator);
+ break;
+ case Qt::DecorationRole:
+ if (index.column() == 0) {
+ if (bookmarkNode->type() == BookmarkNode::Folder)
+ return QApplication::style()->standardIcon(QStyle::SP_DirIcon);
+ return BrowserApplication::instance()->icon(bookmarkNode->url);
+ }
+ }
+
+ return QVariant();
+}
+
+int BookmarksModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.column() > 0) ? 0 : 2;
+}
+
+int BookmarksModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid())
+ return m_bookmarksManager->bookmarks()->children().count();
+
+ const BookmarkNode *item = static_cast<BookmarkNode*>(parent.internalPointer());
+ return item->children().count();
+}
+
+QModelIndex BookmarksModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
+ return QModelIndex();
+
+ // get the parent node
+ BookmarkNode *parentNode = node(parent);
+ return createIndex(row, column, parentNode->children().at(row));
+}
+
+QModelIndex BookmarksModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ BookmarkNode *itemNode = node(index);
+ BookmarkNode *parentNode = (itemNode ? itemNode->parent() : 0);
+ if (!parentNode || parentNode == m_bookmarksManager->bookmarks())
+ return QModelIndex();
+
+ // get the parent's row
+ BookmarkNode *grandParentNode = parentNode->parent();
+ int parentRow = grandParentNode->children().indexOf(parentNode);
+ Q_ASSERT(parentRow >= 0);
+ return createIndex(parentRow, 0, parentNode);
+}
+
+bool BookmarksModel::hasChildren(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return true;
+ const BookmarkNode *parentNode = node(parent);
+ return (parentNode->type() == BookmarkNode::Folder);
+}
+
+Qt::ItemFlags BookmarksModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+
+ BookmarkNode *bookmarkNode = node(index);
+
+ if (bookmarkNode != m_bookmarksManager->menu()
+ && bookmarkNode != m_bookmarksManager->toolbar()) {
+ flags |= Qt::ItemIsDragEnabled;
+ if (bookmarkNode->type() != BookmarkNode::Separator)
+ flags |= Qt::ItemIsEditable;
+ }
+ if (hasChildren(index))
+ flags |= Qt::ItemIsDropEnabled;
+ return flags;
+}
+
+Qt::DropActions BookmarksModel::supportedDropActions () const
+{
+ return Qt::CopyAction | Qt::MoveAction;
+}
+
+#define MIMETYPE QLatin1String("application/bookmarks.xbel")
+
+QStringList BookmarksModel::mimeTypes() const
+{
+ QStringList types;
+ types << MIMETYPE;
+ return types;
+}
+
+QMimeData *BookmarksModel::mimeData(const QModelIndexList &indexes) const
+{
+ QMimeData *mimeData = new QMimeData();
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ foreach (QModelIndex index, indexes) {
+ if (index.column() != 0 || !index.isValid())
+ continue;
+ QByteArray encodedData;
+ QBuffer buffer(&encodedData);
+ buffer.open(QBuffer::ReadWrite);
+ XbelWriter writer;
+ const BookmarkNode *parentNode = node(index);
+ writer.write(&buffer, parentNode);
+ stream << encodedData;
+ }
+ mimeData->setData(MIMETYPE, data);
+ return mimeData;
+}
+
+bool BookmarksModel::dropMimeData(const QMimeData *data,
+ Qt::DropAction action, int row, int column, const QModelIndex &parent)
+{
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (!data->hasFormat(MIMETYPE)
+ || column > 0)
+ return false;
+
+ QByteArray ba = data->data(MIMETYPE);
+ QDataStream stream(&ba, QIODevice::ReadOnly);
+ if (stream.atEnd())
+ return false;
+
+ QUndoStack *undoStack = m_bookmarksManager->undoRedoStack();
+ undoStack->beginMacro(QLatin1String("Move Bookmarks"));
+
+ while (!stream.atEnd()) {
+ QByteArray encodedData;
+ stream >> encodedData;
+ QBuffer buffer(&encodedData);
+ buffer.open(QBuffer::ReadOnly);
+
+ XbelReader reader;
+ BookmarkNode *rootNode = reader.read(&buffer);
+ QList<BookmarkNode*> children = rootNode->children();
+ for (int i = 0; i < children.count(); ++i) {
+ BookmarkNode *bookmarkNode = children.at(i);
+ rootNode->remove(bookmarkNode);
+ row = qMax(0, row);
+ BookmarkNode *parentNode = node(parent);
+ m_bookmarksManager->addBookmark(parentNode, bookmarkNode, row);
+ m_endMacro = true;
+ }
+ delete rootNode;
+ }
+ return true;
+}
+
+bool BookmarksModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid() || (flags(index) & Qt::ItemIsEditable) == 0)
+ return false;
+
+ BookmarkNode *item = node(index);
+
+ switch (role) {
+ case Qt::EditRole:
+ case Qt::DisplayRole:
+ if (index.column() == 0) {
+ m_bookmarksManager->setTitle(item, value.toString());
+ break;
+ }
+ if (index.column() == 1) {
+ m_bookmarksManager->setUrl(item, value.toString());
+ break;
+ }
+ return false;
+ case BookmarksModel::UrlRole:
+ m_bookmarksManager->setUrl(item, value.toUrl().toString());
+ break;
+ case BookmarksModel::UrlStringRole:
+ m_bookmarksManager->setUrl(item, value.toString());
+ break;
+ default:
+ break;
+ return false;
+ }
+
+ return true;
+}
+
+BookmarkNode *BookmarksModel::node(const QModelIndex &index) const
+{
+ BookmarkNode *itemNode = static_cast<BookmarkNode*>(index.internalPointer());
+ if (!itemNode)
+ return m_bookmarksManager->bookmarks();
+ return itemNode;
+}
+
+
+AddBookmarkProxyModel::AddBookmarkProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+}
+
+int AddBookmarkProxyModel::columnCount(const QModelIndex &parent) const
+{
+ return qMin(1, QSortFilterProxyModel::columnCount(parent));
+}
+
+bool AddBookmarkProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
+ return sourceModel()->hasChildren(idx);
+}
+
+AddBookmarkDialog::AddBookmarkDialog(const QString &url, const QString &title, QWidget *parent, BookmarksManager *bookmarkManager)
+ : QDialog(parent)
+ , m_url(url)
+ , m_bookmarksManager(bookmarkManager)
+{
+ setWindowFlags(Qt::Sheet);
+ if (!m_bookmarksManager)
+ m_bookmarksManager = BrowserApplication::bookmarksManager();
+ setupUi(this);
+ QTreeView *view = new QTreeView(this);
+ m_proxyModel = new AddBookmarkProxyModel(this);
+ BookmarksModel *model = m_bookmarksManager->bookmarksModel();
+ m_proxyModel->setSourceModel(model);
+ view->setModel(m_proxyModel);
+ view->expandAll();
+ view->header()->setStretchLastSection(true);
+ view->header()->hide();
+ view->setItemsExpandable(false);
+ view->setRootIsDecorated(false);
+ view->setIndentation(10);
+ location->setModel(m_proxyModel);
+ view->show();
+ location->setView(view);
+ BookmarkNode *menu = m_bookmarksManager->menu();
+ QModelIndex idx = m_proxyModel->mapFromSource(model->index(menu));
+ view->setCurrentIndex(idx);
+ location->setCurrentIndex(idx.row());
+ name->setText(title);
+}
+
+void AddBookmarkDialog::accept()
+{
+ QModelIndex index = location->view()->currentIndex();
+ index = m_proxyModel->mapToSource(index);
+ if (!index.isValid())
+ index = m_bookmarksManager->bookmarksModel()->index(0, 0);
+ BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(index);
+ BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark);
+ bookmark->url = m_url;
+ bookmark->title = name->text();
+ m_bookmarksManager->addBookmark(parent, bookmark);
+ QDialog::accept();
+}
+
+BookmarksMenu::BookmarksMenu(QWidget *parent)
+ : ModelMenu(parent)
+ , m_bookmarksManager(0)
+{
+ connect(this, SIGNAL(activated(const QModelIndex &)),
+ this, SLOT(activated(const QModelIndex &)));
+ setMaxRows(-1);
+ setHoverRole(BookmarksModel::UrlStringRole);
+ setSeparatorRole(BookmarksModel::SeparatorRole);
+}
+
+void BookmarksMenu::activated(const QModelIndex &index)
+{
+ emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
+}
+
+bool BookmarksMenu::prePopulated()
+{
+ m_bookmarksManager = BrowserApplication::bookmarksManager();
+ setModel(m_bookmarksManager->bookmarksModel());
+ setRootIndex(m_bookmarksManager->bookmarksModel()->index(1, 0));
+ // initial actions
+ for (int i = 0; i < m_initialActions.count(); ++i)
+ addAction(m_initialActions.at(i));
+ if (!m_initialActions.isEmpty())
+ addSeparator();
+ createMenu(model()->index(0, 0), 1, this);
+ return true;
+}
+
+void BookmarksMenu::setInitialActions(QList<QAction*> actions)
+{
+ m_initialActions = actions;
+ for (int i = 0; i < m_initialActions.count(); ++i)
+ addAction(m_initialActions.at(i));
+}
+
+BookmarksDialog::BookmarksDialog(QWidget *parent, BookmarksManager *manager)
+ : QDialog(parent)
+{
+ m_bookmarksManager = manager;
+ if (!m_bookmarksManager)
+ m_bookmarksManager = BrowserApplication::bookmarksManager();
+ setupUi(this);
+
+ tree->setUniformRowHeights(true);
+ tree->setSelectionBehavior(QAbstractItemView::SelectRows);
+ tree->setSelectionMode(QAbstractItemView::ContiguousSelection);
+ tree->setTextElideMode(Qt::ElideMiddle);
+ m_bookmarksModel = m_bookmarksManager->bookmarksModel();
+ m_proxyModel = new TreeProxyModel(this);
+ connect(search, SIGNAL(textChanged(QString)),
+ m_proxyModel, SLOT(setFilterFixedString(QString)));
+ connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne()));
+ m_proxyModel->setSourceModel(m_bookmarksModel);
+ tree->setModel(m_proxyModel);
+ tree->setDragDropMode(QAbstractItemView::InternalMove);
+ tree->setExpanded(m_proxyModel->index(0, 0), true);
+ tree->setAlternatingRowColors(true);
+ QFontMetrics fm(font());
+ int header = fm.width(QLatin1Char('m')) * 40;
+ tree->header()->resizeSection(0, header);
+ tree->header()->setStretchLastSection(true);
+ connect(tree, SIGNAL(activated(const QModelIndex&)),
+ this, SLOT(open()));
+ tree->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(customContextMenuRequested(const QPoint &)));
+ connect(addFolderButton, SIGNAL(clicked()),
+ this, SLOT(newFolder()));
+ expandNodes(m_bookmarksManager->bookmarks());
+ setAttribute(Qt::WA_DeleteOnClose);
+}
+
+BookmarksDialog::~BookmarksDialog()
+{
+ if (saveExpandedNodes(tree->rootIndex()))
+ m_bookmarksManager->changeExpanded();
+}
+
+bool BookmarksDialog::saveExpandedNodes(const QModelIndex &parent)
+{
+ bool changed = false;
+ for (int i = 0; i < m_proxyModel->rowCount(parent); ++i) {
+ QModelIndex child = m_proxyModel->index(i, 0, parent);
+ QModelIndex sourceIndex = m_proxyModel->mapToSource(child);
+ BookmarkNode *childNode = m_bookmarksModel->node(sourceIndex);
+ bool wasExpanded = childNode->expanded;
+ if (tree->isExpanded(child)) {
+ childNode->expanded = true;
+ changed |= saveExpandedNodes(child);
+ } else {
+ childNode->expanded = false;
+ }
+ changed |= (wasExpanded != childNode->expanded);
+ }
+ return changed;
+}
+
+void BookmarksDialog::expandNodes(BookmarkNode *node)
+{
+ for (int i = 0; i < node->children().count(); ++i) {
+ BookmarkNode *childNode = node->children()[i];
+ if (childNode->expanded) {
+ QModelIndex idx = m_bookmarksModel->index(childNode);
+ idx = m_proxyModel->mapFromSource(idx);
+ tree->setExpanded(idx, true);
+ expandNodes(childNode);
+ }
+ }
+}
+
+void BookmarksDialog::customContextMenuRequested(const QPoint &pos)
+{
+ QMenu menu;
+ QModelIndex index = tree->indexAt(pos);
+ index = index.sibling(index.row(), 0);
+ if (index.isValid() && !tree->model()->hasChildren(index)) {
+ menu.addAction(tr("Open"), this, SLOT(open()));
+ menu.addSeparator();
+ }
+ menu.addAction(tr("Delete"), tree, SLOT(removeOne()));
+ menu.exec(QCursor::pos());
+}
+
+void BookmarksDialog::open()
+{
+ QModelIndex index = tree->currentIndex();
+ if (!index.parent().isValid())
+ return;
+ emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel::UrlRole).toUrl());
+}
+
+void BookmarksDialog::newFolder()
+{
+ QModelIndex currentIndex = tree->currentIndex();
+ QModelIndex idx = currentIndex;
+ if (idx.isValid() && !idx.model()->hasChildren(idx))
+ idx = idx.parent();
+ if (!idx.isValid())
+ idx = tree->rootIndex();
+ idx = m_proxyModel->mapToSource(idx);
+ BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(idx);
+ BookmarkNode *node = new BookmarkNode(BookmarkNode::Folder);
+ node->title = tr("New Folder");
+ m_bookmarksManager->addBookmark(parent, node, currentIndex.row() + 1);
+}
+
+BookmarksToolBar::BookmarksToolBar(BookmarksModel *model, QWidget *parent)
+ : QToolBar(tr("Bookmark"), parent)
+ , m_bookmarksModel(model)
+{
+ connect(this, SIGNAL(actionTriggered(QAction*)), this, SLOT(triggered(QAction*)));
+ setRootIndex(model->index(0, 0));
+ connect(m_bookmarksModel, SIGNAL(modelReset()), this, SLOT(build()));
+ connect(m_bookmarksModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(build()));
+ connect(m_bookmarksModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(build()));
+ connect(m_bookmarksModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(build()));
+ setAcceptDrops(true);
+}
+
+void BookmarksToolBar::dragEnterEvent(QDragEnterEvent *event)
+{
+ const QMimeData *mimeData = event->mimeData();
+ if (mimeData->hasUrls())
+ event->acceptProposedAction();
+ QToolBar::dragEnterEvent(event);
+}
+
+void BookmarksToolBar::dropEvent(QDropEvent *event)
+{
+ const QMimeData *mimeData = event->mimeData();
+ if (mimeData->hasUrls() && mimeData->hasText()) {
+ QList<QUrl> urls = mimeData->urls();
+ QAction *action = actionAt(event->pos());
+ QString dropText;
+ if (action)
+ dropText = action->text();
+ int row = -1;
+ QModelIndex parentIndex = m_root;
+ for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) {
+ QModelIndex idx = m_bookmarksModel->index(i, 0, m_root);
+ QString title = idx.data().toString();
+ if (title == dropText) {
+ row = i;
+ if (m_bookmarksModel->hasChildren(idx)) {
+ parentIndex = idx;
+ row = -1;
+ }
+ break;
+ }
+ }
+ BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark);
+ bookmark->url = urls.at(0).toString();
+ bookmark->title = mimeData->text();
+
+ BookmarkNode *parent = m_bookmarksModel->node(parentIndex);
+ BookmarksManager *bookmarksManager = m_bookmarksModel->bookmarksManager();
+ bookmarksManager->addBookmark(parent, bookmark, row);
+ event->acceptProposedAction();
+ }
+ QToolBar::dropEvent(event);
+}
+
+
+void BookmarksToolBar::setRootIndex(const QModelIndex &index)
+{
+ m_root = index;
+ build();
+}
+
+QModelIndex BookmarksToolBar::rootIndex() const
+{
+ return m_root;
+}
+
+void BookmarksToolBar::build()
+{
+ clear();
+ for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) {
+ QModelIndex idx = m_bookmarksModel->index(i, 0, m_root);
+ if (m_bookmarksModel->hasChildren(idx)) {
+ QToolButton *button = new QToolButton(this);
+ button->setPopupMode(QToolButton::InstantPopup);
+ button->setArrowType(Qt::DownArrow);
+ button->setText(idx.data().toString());
+ ModelMenu *menu = new ModelMenu(this);
+ connect(menu, SIGNAL(activated(const QModelIndex &)),
+ this, SLOT(activated(const QModelIndex &)));
+ menu->setModel(m_bookmarksModel);
+ menu->setRootIndex(idx);
+ menu->addAction(new QAction(menu));
+ button->setMenu(menu);
+ button->setToolButtonStyle(Qt::ToolButtonTextOnly);
+ QAction *a = addWidget(button);
+ a->setText(idx.data().toString());
+ } else {
+ QAction *action = addAction(idx.data().toString());
+ action->setData(idx.data(BookmarksModel::UrlRole));
+ }
+ }
+}
+
+void BookmarksToolBar::triggered(QAction *action)
+{
+ QVariant v = action->data();
+ if (v.canConvert<QUrl>()) {
+ emit openUrl(v.toUrl());
+ }
+}
+
+void BookmarksToolBar::activated(const QModelIndex &index)
+{
+ emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
+}
+
diff --git a/src/bookmarks.h b/src/bookmarks.h
new file mode 100644
index 00000000..768efd53
--- /dev/null
+++ b/src/bookmarks.h
@@ -0,0 +1,306 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef BOOKMARKS_H
+#define BOOKMARKS_H
+
+#include <QtCore/QObject>
+#include <QtCore/QAbstractItemModel>
+
+#include <QtGui/QUndoCommand>
+
+/*!
+ Bookmark manager, owner of the bookmarks, loads, saves and basic tasks
+ */
+class AutoSaver;
+class BookmarkNode;
+class BookmarksModel;
+class BookmarksManager : public QObject
+{
+ Q_OBJECT
+
+signals:
+ void entryAdded(BookmarkNode *item);
+ void entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item);
+ void entryChanged(BookmarkNode *item);
+
+public:
+ BookmarksManager(QObject *parent = 0);
+ ~BookmarksManager();
+
+ void addBookmark(BookmarkNode *parent, BookmarkNode *node, int row = -1);
+ void removeBookmark(BookmarkNode *node);
+ void setTitle(BookmarkNode *node, const QString &newTitle);
+ void setUrl(BookmarkNode *node, const QString &newUrl);
+ void changeExpanded();
+
+ BookmarkNode *bookmarks();
+ BookmarkNode *menu();
+ BookmarkNode *toolbar();
+
+ BookmarksModel *bookmarksModel();
+ QUndoStack *undoRedoStack() { return &m_commands; };
+
+public slots:
+ void importBookmarks();
+ void exportBookmarks();
+
+private slots:
+ void save() const;
+
+private:
+ void load();
+
+ bool m_loaded;
+ AutoSaver *m_saveTimer;
+ BookmarkNode *m_bookmarkRootNode;
+ BookmarksModel *m_bookmarkModel;
+ QUndoStack m_commands;
+
+ friend class RemoveBookmarksCommand;
+ friend class ChangeBookmarkCommand;
+};
+
+class RemoveBookmarksCommand : public QUndoCommand
+{
+
+public:
+ RemoveBookmarksCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row);
+ ~RemoveBookmarksCommand();
+ void undo();
+ void redo();
+
+protected:
+ int m_row;
+ BookmarksManager *m_bookmarkManagaer;
+ BookmarkNode *m_node;
+ BookmarkNode *m_parent;
+ bool m_done;
+};
+
+class InsertBookmarksCommand : public RemoveBookmarksCommand
+{
+
+public:
+ InsertBookmarksCommand(BookmarksManager *m_bookmarkManagaer,
+ BookmarkNode *parent, BookmarkNode *node, int row);
+ void undo() { RemoveBookmarksCommand::redo(); }
+ void redo() { RemoveBookmarksCommand::undo(); }
+
+};
+
+class ChangeBookmarkCommand : public QUndoCommand
+{
+
+public:
+ ChangeBookmarkCommand(BookmarksManager *m_bookmarkManagaer,
+ BookmarkNode *node, const QString &newValue, bool title);
+ void undo();
+ void redo();
+
+private:
+ BookmarksManager *m_bookmarkManagaer;
+ bool m_title;
+ QString m_oldValue;
+ QString m_newValue;
+ BookmarkNode *m_node;
+};
+
+/*!
+ BookmarksModel is a QAbstractItemModel wrapper around the BookmarkManager
+ */
+#include <QtGui/QIcon>
+class BookmarksModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public slots:
+ void entryAdded(BookmarkNode *item);
+ void entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item);
+ void entryChanged(BookmarkNode *item);
+
+public:
+ enum Roles {
+ TypeRole = Qt::UserRole + 1,
+ UrlRole = Qt::UserRole + 2,
+ UrlStringRole = Qt::UserRole + 3,
+ SeparatorRole = Qt::UserRole + 4
+ };
+
+ BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent = 0);
+ inline BookmarksManager *bookmarksManager() const { return m_bookmarksManager; }
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex& index= QModelIndex()) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ Qt::DropActions supportedDropActions () const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ QStringList mimeTypes() const;
+ bool dropMimeData(const QMimeData *data,
+ Qt::DropAction action, int row, int column, const QModelIndex &parent);
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+
+ BookmarkNode *node(const QModelIndex &index) const;
+ QModelIndex index(BookmarkNode *node) const;
+
+private:
+
+ bool m_endMacro;
+ BookmarksManager *m_bookmarksManager;
+};
+
+// Menu that is dynamically populated from the bookmarks
+#include "modelmenu.h"
+class BookmarksMenu : public ModelMenu
+{
+ Q_OBJECT
+
+signals:
+ void openUrl(const QUrl &url);
+
+public:
+ BookmarksMenu(QWidget *parent = 0);
+ void setInitialActions(QList<QAction*> actions);
+
+protected:
+ bool prePopulated();
+
+private slots:
+ void activated(const QModelIndex &index);
+
+private:
+ BookmarksManager *m_bookmarksManager;
+ QList<QAction*> m_initialActions;
+};
+
+/*
+ Proxy model that filters out the bookmarks so only the folders
+ are left behind. Used in the add bookmark dialog combobox.
+ */
+#include <QtGui/QSortFilterProxyModel>
+class AddBookmarkProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ AddBookmarkProxyModel(QObject * parent = 0);
+ int columnCount(const QModelIndex & parent = QModelIndex()) const;
+
+protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+};
+
+/*!
+ Add bookmark dialog
+ */
+#include "ui_addbookmarkdialog.h"
+class AddBookmarkDialog : public QDialog, public Ui_AddBookmarkDialog
+{
+ Q_OBJECT
+
+public:
+ AddBookmarkDialog(const QString &url, const QString &title, QWidget *parent = 0, BookmarksManager *bookmarkManager = 0);
+
+private slots:
+ void accept();
+
+private:
+ QString m_url;
+ BookmarksManager *m_bookmarksManager;
+ AddBookmarkProxyModel *m_proxyModel;
+};
+
+#include "ui_bookmarks.h"
+class TreeProxyModel;
+class BookmarksDialog : public QDialog, public Ui_BookmarksDialog
+{
+ Q_OBJECT
+
+signals:
+ void openUrl(const QUrl &url);
+
+public:
+ BookmarksDialog(QWidget *parent = 0, BookmarksManager *manager = 0);
+ ~BookmarksDialog();
+
+private slots:
+ void customContextMenuRequested(const QPoint &pos);
+ void open();
+ void newFolder();
+
+private:
+ void expandNodes(BookmarkNode *node);
+ bool saveExpandedNodes(const QModelIndex &parent);
+
+ BookmarksManager *m_bookmarksManager;
+ BookmarksModel *m_bookmarksModel;
+ TreeProxyModel *m_proxyModel;
+};
+
+#include <QtGui/QToolBar>
+class BookmarksToolBar : public QToolBar
+{
+ Q_OBJECT
+
+signals:
+ void openUrl(const QUrl &url);
+
+public:
+ BookmarksToolBar(BookmarksModel *model, QWidget *parent = 0);
+ void setRootIndex(const QModelIndex &index);
+ QModelIndex rootIndex() const;
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dropEvent(QDropEvent *event);
+
+private slots:
+ void triggered(QAction *action);
+ void activated(const QModelIndex &index);
+ void build();
+
+private:
+ BookmarksModel *m_bookmarksModel;
+ QPersistentModelIndex m_root;
+};
+
+#endif // BOOKMARKS_H
diff --git a/src/bookmarks.ui b/src/bookmarks.ui
new file mode 100644
index 00000000..c893e941
--- /dev/null
+++ b/src/bookmarks.ui
@@ -0,0 +1,106 @@
+<ui version="4.0" >
+ <class>BookmarksDialog</class>
+ <widget class="QDialog" name="BookmarksDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>758</width>
+ <height>450</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Bookmarks</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>252</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1" >
+ <widget class="SearchLineEdit" name="search" />
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="EditTreeView" name="tree" />
+ </item>
+ <item row="2" column="0" colspan="2" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QPushButton" name="removeButton" >
+ <property name="text" >
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addFolderButton" >
+ <property name="text" >
+ <string>Add Folder</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SearchLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>searchlineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>EditTreeView</class>
+ <extends>QTreeView</extends>
+ <header>edittreeview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>BookmarksDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>472</x>
+ <y>329</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>461</x>
+ <y>356</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/browser.icns b/src/browser.icns
new file mode 100644
index 00000000..8cc82ba9
--- /dev/null
+++ b/src/browser.icns
Binary files differ
diff --git a/src/browser.ico b/src/browser.ico
new file mode 100644
index 00000000..f15627a1
--- /dev/null
+++ b/src/browser.ico
Binary files differ
diff --git a/src/browser.pro b/src/browser.pro
new file mode 100644
index 00000000..38ef03b5
--- /dev/null
+++ b/src/browser.pro
@@ -0,0 +1,87 @@
+TEMPLATE = app
+TARGET = browser
+QT += webkit network
+
+CONFIG += qt warn_on
+contains(QT_BUILD_PARTS, tools): CONFIG += uitools
+else: DEFINES += QT_NO_UITOOLS
+
+FORMS += \
+ addbookmarkdialog.ui \
+ bookmarks.ui \
+ cookies.ui \
+ cookiesexceptions.ui \
+ downloaditem.ui \
+ downloads.ui \
+ history.ui \
+ passworddialog.ui \
+ proxy.ui \
+ settings.ui
+
+HEADERS += \
+ autosaver.h \
+ bookmarks.h \
+ browserapplication.h \
+ browsermainwindow.h \
+ chasewidget.h \
+ cookiejar.h \
+ downloadmanager.h \
+ edittableview.h \
+ edittreeview.h \
+ history.h \
+ modelmenu.h \
+ networkaccessmanager.h \
+ searchlineedit.h \
+ settings.h \
+ squeezelabel.h \
+ tabwidget.h \
+ toolbarsearch.h \
+ urllineedit.h \
+ webview.h \
+ xbel.h
+
+SOURCES += \
+ autosaver.cpp \
+ bookmarks.cpp \
+ browserapplication.cpp \
+ browsermainwindow.cpp \
+ chasewidget.cpp \
+ cookiejar.cpp \
+ downloadmanager.cpp \
+ edittableview.cpp \
+ edittreeview.cpp \
+ history.cpp \
+ modelmenu.cpp \
+ networkaccessmanager.cpp \
+ searchlineedit.cpp \
+ settings.cpp \
+ squeezelabel.cpp \
+ tabwidget.cpp \
+ toolbarsearch.cpp \
+ urllineedit.cpp \
+ webview.cpp \
+ xbel.cpp \
+ main.cpp
+
+RESOURCES += data/data.qrc htmls/htmls.qrc
+
+build_all:!build_pass {
+ CONFIG -= build_all
+ CONFIG += release
+}
+
+win32 {
+ RC_FILE = browser.rc
+}
+
+mac {
+ ICON = browser.icns
+ QMAKE_INFO_PLIST = Info_mac.plist
+ TARGET = Browser
+}
+
+# install
+target.path = $$[QT_INSTALL_DEMOS]/browser
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS *.plist *.icns *.ico *.rc *.pro *.html *.doc images htmls
+sources.path = $$[QT_INSTALL_DEMOS]/browser
+INSTALLS += target sources
diff --git a/src/browser.rc b/src/browser.rc
new file mode 100644
index 00000000..89a237ce
--- /dev/null
+++ b/src/browser.rc
@@ -0,0 +1,2 @@
+IDI_ICON1 ICON DISCARDABLE "browser.ico"
+
diff --git a/src/browserapplication.cpp b/src/browserapplication.cpp
new file mode 100644
index 00000000..f904ea1c
--- /dev/null
+++ b/src/browserapplication.cpp
@@ -0,0 +1,439 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "browserapplication.h"
+
+#include "bookmarks.h"
+#include "browsermainwindow.h"
+#include "cookiejar.h"
+#include "downloadmanager.h"
+#include "history.h"
+#include "networkaccessmanager.h"
+#include "tabwidget.h"
+#include "webview.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QDir>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QTextStream>
+#include <QtCore/QTranslator>
+
+#include <QtGui/QDesktopServices>
+#include <QtGui/QFileOpenEvent>
+
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+#include <QtNetwork/QNetworkProxy>
+
+#include <QtWebKit/QWebSettings>
+
+#include <QtCore/QDebug>
+
+DownloadManager *BrowserApplication::s_downloadManager = 0;
+HistoryManager *BrowserApplication::s_historyManager = 0;
+NetworkAccessManager *BrowserApplication::s_networkAccessManager = 0;
+BookmarksManager *BrowserApplication::s_bookmarksManager = 0;
+
+BrowserApplication::BrowserApplication(int &argc, char **argv)
+ : QApplication(argc, argv)
+ , m_localServer(0)
+{
+ QCoreApplication::setOrganizationName(QLatin1String("Trolltech"));
+ QCoreApplication::setApplicationName(QLatin1String("demobrowser"));
+ QCoreApplication::setApplicationVersion(QLatin1String("0.1"));
+#ifdef Q_WS_QWS
+ // Use a different server name for QWS so we can run an X11
+ // browser and a QWS browser in parallel on the same machine for
+ // debugging
+ QString serverName = QCoreApplication::applicationName() + QLatin1String("_qws");
+#else
+ QString serverName = QCoreApplication::applicationName();
+#endif
+ QLocalSocket socket;
+ socket.connectToServer(serverName);
+ if (socket.waitForConnected(500)) {
+ QTextStream stream(&socket);
+ QStringList args = QCoreApplication::arguments();
+ if (args.count() > 1)
+ stream << args.last();
+ else
+ stream << QString();
+ stream.flush();
+ socket.waitForBytesWritten();
+ return;
+ }
+
+#if defined(Q_WS_MAC)
+ QApplication::setQuitOnLastWindowClosed(false);
+#else
+ QApplication::setQuitOnLastWindowClosed(true);
+#endif
+
+ m_localServer = new QLocalServer(this);
+ connect(m_localServer, SIGNAL(newConnection()),
+ this, SLOT(newLocalSocketConnection()));
+ if (!m_localServer->listen(serverName)) {
+ if (m_localServer->serverError() == QAbstractSocket::AddressInUseError
+ && QFile::exists(m_localServer->serverName())) {
+ QFile::remove(m_localServer->serverName());
+ m_localServer->listen(serverName);
+ }
+ }
+
+ QDesktopServices::setUrlHandler(QLatin1String("http"), this, "openUrl");
+ QString localSysName = QLocale::system().name();
+
+ installTranslator(QLatin1String("qt_") + localSysName);
+
+ QSettings settings;
+ settings.beginGroup(QLatin1String("sessions"));
+ m_lastSession = settings.value(QLatin1String("lastSession")).toByteArray();
+ settings.endGroup();
+
+#if defined(Q_WS_MAC)
+ connect(this, SIGNAL(lastWindowClosed()),
+ this, SLOT(lastWindowClosed()));
+#endif
+
+ QTimer::singleShot(0, this, SLOT(postLaunch()));
+}
+
+BrowserApplication::~BrowserApplication()
+{
+ delete s_downloadManager;
+ qDeleteAll(m_mainWindows);
+ delete s_networkAccessManager;
+ delete s_bookmarksManager;
+}
+
+#if defined(Q_WS_MAC)
+void BrowserApplication::lastWindowClosed()
+{
+ clean();
+ BrowserMainWindow *mw = new BrowserMainWindow;
+ mw->slotHome();
+ m_mainWindows.prepend(mw);
+}
+#endif
+
+BrowserApplication *BrowserApplication::instance()
+{
+ return (static_cast<BrowserApplication *>(QCoreApplication::instance()));
+}
+
+#if defined(Q_WS_MAC)
+#include <QtGui/QMessageBox>
+void BrowserApplication::quitBrowser()
+{
+ clean();
+ int tabCount = 0;
+ for (int i = 0; i < m_mainWindows.count(); ++i) {
+ tabCount =+ m_mainWindows.at(i)->tabWidget()->count();
+ }
+
+ if (tabCount > 1) {
+ int ret = QMessageBox::warning(mainWindow(), QString(),
+ tr("There are %1 windows and %2 tabs open\n"
+ "Do you want to quit anyway?").arg(m_mainWindows.count()).arg(tabCount),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No);
+ if (ret == QMessageBox::No)
+ return;
+ }
+
+ exit(0);
+}
+#endif
+
+/*!
+ Any actions that can be delayed until the window is visible
+ */
+void BrowserApplication::postLaunch()
+{
+ QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ if (directory.isEmpty())
+ directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName();
+ QWebSettings::setIconDatabasePath(directory);
+
+// setWindowIcon(QIcon(QLatin1String(":browser.svg"))); // FIXME setICON
+
+ loadSettings();
+
+ // newMainWindow() needs to be called in main() for this to happen
+ if (m_mainWindows.count() > 0) {
+ QStringList args = QCoreApplication::arguments();
+ if (args.count() > 1)
+ mainWindow()->loadPage(args.last());
+ else
+ mainWindow()->slotHome();
+ }
+ BrowserApplication::historyManager();
+}
+
+void BrowserApplication::loadSettings()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("websettings"));
+
+ QWebSettings *defaultSettings = QWebSettings::globalSettings();
+ QString standardFontFamily = defaultSettings->fontFamily(QWebSettings::StandardFont);
+ int standardFontSize = defaultSettings->fontSize(QWebSettings::DefaultFontSize);
+ QFont standardFont = QFont(standardFontFamily, standardFontSize);
+ standardFont = qVariantValue<QFont>(settings.value(QLatin1String("standardFont"), standardFont));
+ defaultSettings->setFontFamily(QWebSettings::StandardFont, standardFont.family());
+ defaultSettings->setFontSize(QWebSettings::DefaultFontSize, standardFont.pointSize());
+
+ QString fixedFontFamily = defaultSettings->fontFamily(QWebSettings::FixedFont);
+ int fixedFontSize = defaultSettings->fontSize(QWebSettings::DefaultFixedFontSize);
+ QFont fixedFont = QFont(fixedFontFamily, fixedFontSize);
+ fixedFont = qVariantValue<QFont>(settings.value(QLatin1String("fixedFont"), fixedFont));
+ defaultSettings->setFontFamily(QWebSettings::FixedFont, fixedFont.family());
+ defaultSettings->setFontSize(QWebSettings::DefaultFixedFontSize, fixedFont.pointSize());
+
+ defaultSettings->setAttribute(QWebSettings::JavascriptEnabled, settings.value(QLatin1String("enableJavascript"), true).toBool());
+ defaultSettings->setAttribute(QWebSettings::PluginsEnabled, settings.value(QLatin1String("enablePlugins"), true).toBool());
+
+ QUrl url = settings.value(QLatin1String("userStyleSheet")).toUrl();
+ defaultSettings->setUserStyleSheetUrl(url);
+
+ settings.endGroup();
+}
+
+QList<BrowserMainWindow*> BrowserApplication::mainWindows()
+{
+ clean();
+ QList<BrowserMainWindow*> list;
+ for (int i = 0; i < m_mainWindows.count(); ++i)
+ list.append(m_mainWindows.at(i));
+ return list;
+}
+
+void BrowserApplication::clean()
+{
+ // cleanup any deleted main windows first
+ for (int i = m_mainWindows.count() - 1; i >= 0; --i)
+ if (m_mainWindows.at(i).isNull())
+ m_mainWindows.removeAt(i);
+}
+
+void BrowserApplication::saveSession()
+{
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
+ return;
+
+ clean();
+
+ QSettings settings;
+ settings.beginGroup(QLatin1String("sessions"));
+
+ QByteArray data;
+ QBuffer buffer(&data);
+ QDataStream stream(&buffer);
+ buffer.open(QIODevice::ReadWrite);
+
+ stream << m_mainWindows.count();
+ for (int i = 0; i < m_mainWindows.count(); ++i)
+ stream << m_mainWindows.at(i)->saveState();
+ settings.setValue(QLatin1String("lastSession"), data);
+ settings.endGroup();
+}
+
+bool BrowserApplication::canRestoreSession() const
+{
+ return !m_lastSession.isEmpty();
+}
+
+void BrowserApplication::restoreLastSession()
+{
+ QList<QByteArray> windows;
+ QBuffer buffer(&m_lastSession);
+ QDataStream stream(&buffer);
+ buffer.open(QIODevice::ReadOnly);
+ int windowCount;
+ stream >> windowCount;
+ for (int i = 0; i < windowCount; ++i) {
+ QByteArray windowState;
+ stream >> windowState;
+ windows.append(windowState);
+ }
+ for (int i = 0; i < windows.count(); ++i) {
+ BrowserMainWindow *newWindow = 0;
+ if (m_mainWindows.count() == 1
+ && mainWindow()->tabWidget()->count() == 1
+ && mainWindow()->currentTab()->url() == QUrl()) {
+ newWindow = mainWindow();
+ } else {
+ newWindow = newMainWindow();
+ }
+ newWindow->restoreState(windows.at(i));
+ }
+}
+
+bool BrowserApplication::isTheOnlyBrowser() const
+{
+ return (m_localServer != 0);
+}
+
+void BrowserApplication::installTranslator(const QString &name)
+{
+ QTranslator *translator = new QTranslator(this);
+ translator->load(name, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+ QApplication::installTranslator(translator);
+}
+
+#if defined(Q_WS_MAC)
+bool BrowserApplication::event(QEvent* event)
+{
+ switch (event->type()) {
+ case QEvent::ApplicationActivate: {
+ clean();
+ if (!m_mainWindows.isEmpty()) {
+ BrowserMainWindow *mw = mainWindow();
+ if (mw && !mw->isMinimized()) {
+ mainWindow()->show();
+ }
+ return true;
+ }
+ }
+ case QEvent::FileOpen:
+ if (!m_mainWindows.isEmpty()) {
+ mainWindow()->loadPage(static_cast<QFileOpenEvent *>(event)->file());
+ return true;
+ }
+ default:
+ break;
+ }
+ return QApplication::event(event);
+}
+#endif
+
+void BrowserApplication::openUrl(const QUrl &url)
+{
+ mainWindow()->loadPage(url.toString());
+}
+
+BrowserMainWindow *BrowserApplication::newMainWindow()
+{
+ BrowserMainWindow *browser = new BrowserMainWindow();
+ m_mainWindows.prepend(browser);
+ browser->show();
+ return browser;
+}
+
+BrowserMainWindow *BrowserApplication::mainWindow()
+{
+ clean();
+ if (m_mainWindows.isEmpty())
+ newMainWindow();
+ return m_mainWindows[0];
+}
+
+void BrowserApplication::newLocalSocketConnection()
+{
+ QLocalSocket *socket = m_localServer->nextPendingConnection();
+ if (!socket)
+ return;
+ socket->waitForReadyRead(1000);
+ QTextStream stream(socket);
+ QString url;
+ stream >> url;
+ if (!url.isEmpty()) {
+ QSettings settings;
+ settings.beginGroup(QLatin1String("general"));
+ int openLinksIn = settings.value(QLatin1String("openLinksIn"), 0).toInt();
+ settings.endGroup();
+ if (openLinksIn == 1)
+ newMainWindow();
+ else
+ mainWindow()->tabWidget()->newTab();
+ openUrl(url);
+ }
+ delete socket;
+ mainWindow()->raise();
+ mainWindow()->activateWindow();
+}
+
+CookieJar *BrowserApplication::cookieJar()
+{
+ return (CookieJar*)networkAccessManager()->cookieJar();
+}
+
+DownloadManager *BrowserApplication::downloadManager()
+{
+ if (!s_downloadManager) {
+ s_downloadManager = new DownloadManager();
+ }
+ return s_downloadManager;
+}
+
+NetworkAccessManager *BrowserApplication::networkAccessManager()
+{
+ if (!s_networkAccessManager) {
+ s_networkAccessManager = new NetworkAccessManager();
+ s_networkAccessManager->setCookieJar(new CookieJar);
+ }
+ return s_networkAccessManager;
+}
+
+HistoryManager *BrowserApplication::historyManager()
+{
+ if (!s_historyManager) {
+ s_historyManager = new HistoryManager();
+ QWebHistoryInterface::setDefaultInterface(s_historyManager);
+ }
+ return s_historyManager;
+}
+
+BookmarksManager *BrowserApplication::bookmarksManager()
+{
+ if (!s_bookmarksManager) {
+ s_bookmarksManager = new BookmarksManager;
+ }
+ return s_bookmarksManager;
+}
+
+QIcon BrowserApplication::icon(const QUrl &url) const
+{
+ QIcon icon = QWebSettings::iconForUrl(url);
+ if (!icon.isNull())
+ return icon.pixmap(16, 16);
+ if (m_defaultIcon.isNull())
+ m_defaultIcon = QIcon(QLatin1String(":defaulticon.png"));
+ return m_defaultIcon.pixmap(16, 16);
+}
diff --git a/src/browserapplication.h b/src/browserapplication.h
new file mode 100644
index 00000000..19d02a47
--- /dev/null
+++ b/src/browserapplication.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef BROWSERAPPLICATION_H
+#define BROWSERAPPLICATION_H
+
+#include <QtGui/QApplication>
+
+#include <QtCore/QUrl>
+#include <QtCore/QPointer>
+
+#include <QtGui/QIcon>
+
+QT_BEGIN_NAMESPACE
+class QLocalServer;
+QT_END_NAMESPACE
+
+class BookmarksManager;
+class BrowserMainWindow;
+class CookieJar;
+class DownloadManager;
+class HistoryManager;
+class NetworkAccessManager;
+class BrowserApplication : public QApplication
+{
+ Q_OBJECT
+
+public:
+ BrowserApplication(int &argc, char **argv);
+ ~BrowserApplication();
+ static BrowserApplication *instance();
+ void loadSettings();
+
+ bool isTheOnlyBrowser() const;
+ BrowserMainWindow *mainWindow();
+ QList<BrowserMainWindow*> mainWindows();
+ QIcon icon(const QUrl &url) const;
+
+ void saveSession();
+ bool canRestoreSession() const;
+
+ static HistoryManager *historyManager();
+ static CookieJar *cookieJar();
+ static DownloadManager *downloadManager();
+ static NetworkAccessManager *networkAccessManager();
+ static BookmarksManager *bookmarksManager();
+
+#if defined(Q_WS_MAC)
+ bool event(QEvent *event);
+#endif
+
+public slots:
+ BrowserMainWindow *newMainWindow();
+ void restoreLastSession();
+#if defined(Q_WS_MAC)
+ void lastWindowClosed();
+ void quitBrowser();
+#endif
+
+private slots:
+ void postLaunch();
+ void openUrl(const QUrl &url);
+ void newLocalSocketConnection();
+
+private:
+ void clean();
+ void installTranslator(const QString &name);
+
+ static HistoryManager *s_historyManager;
+ static DownloadManager *s_downloadManager;
+ static NetworkAccessManager *s_networkAccessManager;
+ static BookmarksManager *s_bookmarksManager;
+
+ QList<QPointer<BrowserMainWindow> > m_mainWindows;
+ QLocalServer *m_localServer;
+ QByteArray m_lastSession;
+ mutable QIcon m_defaultIcon;
+};
+
+#endif // BROWSERAPPLICATION_H
+
diff --git a/src/browsermainwindow.cpp b/src/browsermainwindow.cpp
new file mode 100644
index 00000000..3d1617e4
--- /dev/null
+++ b/src/browsermainwindow.cpp
@@ -0,0 +1,969 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "browsermainwindow.h"
+
+#include "autosaver.h"
+#include "bookmarks.h"
+#include "browserapplication.h"
+#include "chasewidget.h"
+#include "downloadmanager.h"
+#include "history.h"
+#include "settings.h"
+#include "tabwidget.h"
+#include "toolbarsearch.h"
+#include "ui_passworddialog.h"
+#include "webview.h"
+
+#include <QtCore/QSettings>
+
+#include <QtGui/QDesktopWidget>
+#include <QtGui/QFileDialog>
+#include <QtGui/QPlainTextEdit>
+#include <QtGui/QPrintDialog>
+#include <QtGui/QPrintPreviewDialog>
+#include <QtGui/QPrinter>
+#include <QtGui/QMenuBar>
+#include <QtGui/QMessageBox>
+#include <QtGui/QStatusBar>
+#include <QtGui/QToolBar>
+#include <QtGui/QInputDialog>
+
+#include <QtWebKit/QWebFrame>
+#include <QtWebKit/QWebHistory>
+
+#include <QtCore/QDebug>
+
+BrowserMainWindow::BrowserMainWindow(QWidget *parent, Qt::WindowFlags flags)
+ : QMainWindow(parent, flags)
+ , m_tabWidget(new TabWidget(this))
+ , m_autoSaver(new AutoSaver(this))
+ , m_historyBack(0)
+ , m_historyForward(0)
+ , m_stop(0)
+ , m_reload(0)
+{
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ statusBar()->setSizeGripEnabled(true);
+ setupMenu();
+ setupToolBar();
+
+ QWidget *centralWidget = new QWidget(this);
+ BookmarksModel *boomarksModel = BrowserApplication::bookmarksManager()->bookmarksModel();
+ m_bookmarksToolbar = new BookmarksToolBar(boomarksModel, this);
+ connect(m_bookmarksToolbar, SIGNAL(openUrl(const QUrl&)),
+ m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&)));
+ connect(m_bookmarksToolbar->toggleViewAction(), SIGNAL(toggled(bool)),
+ this, SLOT(updateBookmarksToolbarActionText(bool)));
+
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->setSpacing(0);
+ layout->setMargin(0);
+#if defined(Q_WS_MAC)
+ layout->addWidget(m_bookmarksToolbar);
+ layout->addWidget(new QWidget); // <- OS X tab widget style bug
+#else
+ addToolBarBreak();
+ addToolBar(m_bookmarksToolbar);
+#endif
+ layout->addWidget(m_tabWidget);
+ centralWidget->setLayout(layout);
+ setCentralWidget(centralWidget);
+
+ connect(m_tabWidget, SIGNAL(loadPage(const QString &)),
+ this, SLOT(loadPage(const QString &)));
+ connect(m_tabWidget, SIGNAL(setCurrentTitle(const QString &)),
+ this, SLOT(slotUpdateWindowTitle(const QString &)));
+ connect(m_tabWidget, SIGNAL(showStatusBarMessage(const QString&)),
+ statusBar(), SLOT(showMessage(const QString&)));
+ connect(m_tabWidget, SIGNAL(linkHovered(const QString&)),
+ statusBar(), SLOT(showMessage(const QString&)));
+ connect(m_tabWidget, SIGNAL(loadProgress(int)),
+ this, SLOT(slotLoadProgress(int)));
+ connect(m_tabWidget, SIGNAL(tabsChanged()),
+ m_autoSaver, SLOT(changeOccurred()));
+ connect(m_tabWidget, SIGNAL(geometryChangeRequested(const QRect &)),
+ this, SLOT(geometryChangeRequested(const QRect &)));
+ connect(m_tabWidget, SIGNAL(printRequested(QWebFrame *)),
+ this, SLOT(printRequested(QWebFrame *)));
+ connect(m_tabWidget, SIGNAL(menuBarVisibilityChangeRequested(bool)),
+ menuBar(), SLOT(setVisible(bool)));
+ connect(m_tabWidget, SIGNAL(statusBarVisibilityChangeRequested(bool)),
+ statusBar(), SLOT(setVisible(bool)));
+ connect(m_tabWidget, SIGNAL(toolBarVisibilityChangeRequested(bool)),
+ m_navigationBar, SLOT(setVisible(bool)));
+ connect(m_tabWidget, SIGNAL(toolBarVisibilityChangeRequested(bool)),
+ m_bookmarksToolbar, SLOT(setVisible(bool)));
+#if defined(Q_WS_MAC)
+ connect(m_tabWidget, SIGNAL(lastTabClosed()),
+ this, SLOT(close()));
+#else
+ connect(m_tabWidget, SIGNAL(lastTabClosed()),
+ m_tabWidget, SLOT(newTab()));
+#endif
+
+ slotUpdateWindowTitle();
+ loadDefaultState();
+ m_tabWidget->newTab();
+
+ int size = m_tabWidget->lineEditStack()->sizeHint().height();
+ m_navigationBar->setIconSize(QSize(size, size));
+}
+
+BrowserMainWindow::~BrowserMainWindow()
+{
+ m_autoSaver->changeOccurred();
+ m_autoSaver->saveIfNeccessary();
+}
+
+void BrowserMainWindow::loadDefaultState()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("BrowserMainWindow"));
+ QByteArray data = settings.value(QLatin1String("defaultState")).toByteArray();
+ restoreState(data);
+ settings.endGroup();
+}
+
+QSize BrowserMainWindow::sizeHint() const
+{
+ QRect desktopRect = QApplication::desktop()->screenGeometry();
+ QSize size = desktopRect.size() * 0.9;
+ return size;
+}
+
+void BrowserMainWindow::save()
+{
+ BrowserApplication::instance()->saveSession();
+
+ QSettings settings;
+ settings.beginGroup(QLatin1String("BrowserMainWindow"));
+ QByteArray data = saveState(false);
+ settings.setValue(QLatin1String("defaultState"), data);
+ settings.endGroup();
+}
+
+static const qint32 BrowserMainWindowMagic = 0xba;
+
+QByteArray BrowserMainWindow::saveState(bool withTabs) const
+{
+ int version = 2;
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+
+ stream << qint32(BrowserMainWindowMagic);
+ stream << qint32(version);
+
+ stream << size();
+ stream << !m_navigationBar->isHidden();
+ stream << !m_bookmarksToolbar->isHidden();
+ stream << !statusBar()->isHidden();
+ if (withTabs)
+ stream << tabWidget()->saveState();
+ else
+ stream << QByteArray();
+ return data;
+}
+
+bool BrowserMainWindow::restoreState(const QByteArray &state)
+{
+ int version = 2;
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+ if (stream.atEnd())
+ return false;
+
+ qint32 marker;
+ qint32 v;
+ stream >> marker;
+ stream >> v;
+ if (marker != BrowserMainWindowMagic || v != version)
+ return false;
+
+ QSize size;
+ bool showToolbar;
+ bool showBookmarksBar;
+ bool showStatusbar;
+ QByteArray tabState;
+
+ stream >> size;
+ stream >> showToolbar;
+ stream >> showBookmarksBar;
+ stream >> showStatusbar;
+ stream >> tabState;
+
+ resize(size);
+
+ m_navigationBar->setVisible(showToolbar);
+ updateToolbarActionText(showToolbar);
+
+ m_bookmarksToolbar->setVisible(showBookmarksBar);
+ updateBookmarksToolbarActionText(showBookmarksBar);
+
+ statusBar()->setVisible(showStatusbar);
+ updateStatusbarActionText(showStatusbar);
+
+ if (!tabWidget()->restoreState(tabState))
+ return false;
+
+ return true;
+}
+
+void BrowserMainWindow::setupMenu()
+{
+ new QShortcut(QKeySequence(Qt::Key_F6), this, SLOT(slotSwapFocus()));
+
+ // File
+ QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+
+ fileMenu->addAction(tr("&New Window"), this, SLOT(slotFileNew()), QKeySequence::New);
+ fileMenu->addAction(m_tabWidget->newTabAction());
+ fileMenu->addAction(tr("&Open File..."), this, SLOT(slotFileOpen()), QKeySequence::Open);
+ fileMenu->addAction(tr("Open &Location..."), this,
+ SLOT(slotSelectLineEdit()), QKeySequence(Qt::ControlModifier + Qt::Key_L));
+ fileMenu->addSeparator();
+ fileMenu->addAction(m_tabWidget->closeTabAction());
+ fileMenu->addSeparator();
+ fileMenu->addAction(tr("&Save As..."), this,
+ SLOT(slotFileSaveAs()), QKeySequence(QKeySequence::Save));
+ fileMenu->addSeparator();
+ BookmarksManager *bookmarksManager = BrowserApplication::bookmarksManager();
+ fileMenu->addAction(tr("&Import Bookmarks..."), bookmarksManager, SLOT(importBookmarks()));
+ fileMenu->addAction(tr("&Export Bookmarks..."), bookmarksManager, SLOT(exportBookmarks()));
+ fileMenu->addSeparator();
+ fileMenu->addAction(tr("P&rint Preview..."), this, SLOT(slotFilePrintPreview()));
+ fileMenu->addAction(tr("&Print..."), this, SLOT(slotFilePrint()), QKeySequence::Print);
+ fileMenu->addSeparator();
+ QAction *action = fileMenu->addAction(tr("Private &Browsing..."), this, SLOT(slotPrivateBrowsing()));
+ action->setCheckable(true);
+ fileMenu->addSeparator();
+
+#if defined(Q_WS_MAC)
+ fileMenu->addAction(tr("&Quit"), BrowserApplication::instance(), SLOT(quitBrowser()), QKeySequence(Qt::CTRL | Qt::Key_Q));
+#else
+ fileMenu->addAction(tr("&Quit"), this, SLOT(close()), QKeySequence(Qt::CTRL | Qt::Key_Q));
+#endif
+
+ // Edit
+ QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
+ QAction *m_undo = editMenu->addAction(tr("&Undo"));
+ m_undo->setShortcuts(QKeySequence::Undo);
+ m_tabWidget->addWebAction(m_undo, QWebPage::Undo);
+ QAction *m_redo = editMenu->addAction(tr("&Redo"));
+ m_redo->setShortcuts(QKeySequence::Redo);
+ m_tabWidget->addWebAction(m_redo, QWebPage::Redo);
+ editMenu->addSeparator();
+ QAction *m_cut = editMenu->addAction(tr("Cu&t"));
+ m_cut->setShortcuts(QKeySequence::Cut);
+ m_tabWidget->addWebAction(m_cut, QWebPage::Cut);
+ QAction *m_copy = editMenu->addAction(tr("&Copy"));
+ m_copy->setShortcuts(QKeySequence::Copy);
+ m_tabWidget->addWebAction(m_copy, QWebPage::Copy);
+ QAction *m_paste = editMenu->addAction(tr("&Paste"));
+ m_paste->setShortcuts(QKeySequence::Paste);
+ m_tabWidget->addWebAction(m_paste, QWebPage::Paste);
+ editMenu->addSeparator();
+
+ QAction *m_find = editMenu->addAction(tr("&Find"));
+ m_find->setShortcuts(QKeySequence::Find);
+ connect(m_find, SIGNAL(triggered()), this, SLOT(slotEditFind()));
+ new QShortcut(QKeySequence(Qt::Key_Slash), this, SLOT(slotEditFind()));
+
+ QAction *m_findNext = editMenu->addAction(tr("&Find Next"));
+ m_findNext->setShortcuts(QKeySequence::FindNext);
+ connect(m_findNext, SIGNAL(triggered()), this, SLOT(slotEditFindNext()));
+
+ QAction *m_findPrevious = editMenu->addAction(tr("&Find Previous"));
+ m_findPrevious->setShortcuts(QKeySequence::FindPrevious);
+ connect(m_findPrevious, SIGNAL(triggered()), this, SLOT(slotEditFindPrevious()));
+
+ editMenu->addSeparator();
+ editMenu->addAction(tr("&Preferences"), this, SLOT(slotPreferences()), tr("Ctrl+,"));
+
+ // View
+ QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
+
+ m_viewBookmarkBar = new QAction(this);
+ updateBookmarksToolbarActionText(true);
+ m_viewBookmarkBar->setShortcut(tr("Shift+Ctrl+B"));
+ connect(m_viewBookmarkBar, SIGNAL(triggered()), this, SLOT(slotViewBookmarksBar()));
+ viewMenu->addAction(m_viewBookmarkBar);
+
+ m_viewToolbar = new QAction(this);
+ updateToolbarActionText(true);
+ m_viewToolbar->setShortcut(tr("Ctrl+|"));
+ connect(m_viewToolbar, SIGNAL(triggered()), this, SLOT(slotViewToolbar()));
+ viewMenu->addAction(m_viewToolbar);
+
+ m_viewStatusbar = new QAction(this);
+ updateStatusbarActionText(true);
+ m_viewStatusbar->setShortcut(tr("Ctrl+/"));
+ connect(m_viewStatusbar, SIGNAL(triggered()), this, SLOT(slotViewStatusbar()));
+ viewMenu->addAction(m_viewStatusbar);
+
+ viewMenu->addSeparator();
+
+ m_stop = viewMenu->addAction(tr("&Stop"));
+ QList<QKeySequence> shortcuts;
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Period));
+ shortcuts.append(Qt::Key_Escape);
+ m_stop->setShortcuts(shortcuts);
+ m_tabWidget->addWebAction(m_stop, QWebPage::Stop);
+
+ m_reload = viewMenu->addAction(tr("Reload Page"));
+ m_reload->setShortcuts(QKeySequence::Refresh);
+ m_tabWidget->addWebAction(m_reload, QWebPage::Reload);
+
+ viewMenu->addAction(tr("&Make Text Bigger"), this, SLOT(slotViewTextBigger()), QKeySequence(Qt::CTRL | Qt::Key_Plus));
+ viewMenu->addAction(tr("&Make Text Normal"), this, SLOT(slotViewTextNormal()), QKeySequence(Qt::CTRL | Qt::Key_0));
+ viewMenu->addAction(tr("&Make Text Smaller"), this, SLOT(slotViewTextSmaller()), QKeySequence(Qt::CTRL | Qt::Key_Minus));
+
+ viewMenu->addSeparator();
+ viewMenu->addAction(tr("Page S&ource"), this, SLOT(slotViewPageSource()), tr("Ctrl+Alt+U"));
+ QAction *a = viewMenu->addAction(tr("&Full Screen"), this, SLOT(slotViewFullScreen(bool)), Qt::Key_F11);
+ a->setCheckable(true);
+
+ // History
+ HistoryMenu *historyMenu = new HistoryMenu(this);
+ connect(historyMenu, SIGNAL(openUrl(const QUrl&)),
+ m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&)));
+ connect(historyMenu, SIGNAL(hovered(const QString&)), this,
+ SLOT(slotUpdateStatusbar(const QString&)));
+ historyMenu->setTitle(tr("Hi&story"));
+ menuBar()->addMenu(historyMenu);
+ QList<QAction*> historyActions;
+
+ m_historyBack = new QAction(tr("Back"), this);
+ m_tabWidget->addWebAction(m_historyBack, QWebPage::Back);
+ m_historyBack->setShortcuts(QKeySequence::Back);
+ m_historyBack->setIconVisibleInMenu(false);
+
+ m_historyForward = new QAction(tr("Forward"), this);
+ m_tabWidget->addWebAction(m_historyForward, QWebPage::Forward);
+ m_historyForward->setShortcuts(QKeySequence::Forward);
+ m_historyForward->setIconVisibleInMenu(false);
+
+ QAction *m_historyHome = new QAction(tr("Home"), this);
+ connect(m_historyHome, SIGNAL(triggered()), this, SLOT(slotHome()));
+ m_historyHome->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_H));
+
+ m_restoreLastSession = new QAction(tr("Restore Last Session"), this);
+ connect(m_restoreLastSession, SIGNAL(triggered()), BrowserApplication::instance(), SLOT(restoreLastSession()));
+ m_restoreLastSession->setEnabled(BrowserApplication::instance()->canRestoreSession());
+
+ historyActions.append(m_historyBack);
+ historyActions.append(m_historyForward);
+ historyActions.append(m_historyHome);
+ historyActions.append(m_tabWidget->recentlyClosedTabsAction());
+ historyActions.append(m_restoreLastSession);
+ historyMenu->setInitialActions(historyActions);
+
+ // Bookmarks
+ BookmarksMenu *bookmarksMenu = new BookmarksMenu(this);
+ connect(bookmarksMenu, SIGNAL(openUrl(const QUrl&)),
+ m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&)));
+ connect(bookmarksMenu, SIGNAL(hovered(const QString&)),
+ this, SLOT(slotUpdateStatusbar(const QString&)));
+ bookmarksMenu->setTitle(tr("&Bookmarks"));
+ menuBar()->addMenu(bookmarksMenu);
+
+ QList<QAction*> bookmarksActions;
+
+ QAction *showAllBookmarksAction = new QAction(tr("Show All Bookmarks"), this);
+ connect(showAllBookmarksAction, SIGNAL(triggered()), this, SLOT(slotShowBookmarksDialog()));
+ m_addBookmark = new QAction(QIcon(QLatin1String(":addbookmark.png")), tr("Add Bookmark..."), this);
+ m_addBookmark->setIconVisibleInMenu(false);
+
+ connect(m_addBookmark, SIGNAL(triggered()), this, SLOT(slotAddBookmark()));
+ m_addBookmark->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_D));
+
+ bookmarksActions.append(showAllBookmarksAction);
+ bookmarksActions.append(m_addBookmark);
+ bookmarksMenu->setInitialActions(bookmarksActions);
+
+ // Window
+ m_windowMenu = menuBar()->addMenu(tr("&Window"));
+ connect(m_windowMenu, SIGNAL(aboutToShow()),
+ this, SLOT(slotAboutToShowWindowMenu()));
+ slotAboutToShowWindowMenu();
+
+ QMenu *toolsMenu = menuBar()->addMenu(tr("&Tools"));
+ toolsMenu->addAction(tr("Web &Search"), this, SLOT(slotWebSearch()), QKeySequence(tr("Ctrl+K", "Web Search")));
+#ifndef Q_CC_MINGW
+ a = toolsMenu->addAction(tr("Enable Web &Inspector"), this, SLOT(slotToggleInspector(bool)));
+ a->setCheckable(true);
+#endif
+
+ QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+ helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
+ helpMenu->addAction(tr("About &Demo Browser"), this, SLOT(slotAboutApplication()));
+}
+
+void BrowserMainWindow::setupToolBar()
+{
+ setUnifiedTitleAndToolBarOnMac(true);
+ m_navigationBar = addToolBar(tr("Navigation"));
+ connect(m_navigationBar->toggleViewAction(), SIGNAL(toggled(bool)),
+ this, SLOT(updateToolbarActionText(bool)));
+
+ m_historyBack->setIcon(style()->standardIcon(QStyle::SP_ArrowBack, 0, this));
+ m_historyBackMenu = new QMenu(this);
+ m_historyBack->setMenu(m_historyBackMenu);
+ connect(m_historyBackMenu, SIGNAL(aboutToShow()),
+ this, SLOT(slotAboutToShowBackMenu()));
+ connect(m_historyBackMenu, SIGNAL(triggered(QAction *)),
+ this, SLOT(slotOpenActionUrl(QAction *)));
+ m_navigationBar->addAction(m_historyBack);
+
+ m_historyForward->setIcon(style()->standardIcon(QStyle::SP_ArrowForward, 0, this));
+ m_historyForwardMenu = new QMenu(this);
+ connect(m_historyForwardMenu, SIGNAL(aboutToShow()),
+ this, SLOT(slotAboutToShowForwardMenu()));
+ connect(m_historyForwardMenu, SIGNAL(triggered(QAction *)),
+ this, SLOT(slotOpenActionUrl(QAction *)));
+ m_historyForward->setMenu(m_historyForwardMenu);
+ m_navigationBar->addAction(m_historyForward);
+
+ m_stopReload = new QAction(this);
+ m_reloadIcon = style()->standardIcon(QStyle::SP_BrowserReload);
+ m_stopReload->setIcon(m_reloadIcon);
+
+ m_navigationBar->addAction(m_stopReload);
+
+ m_navigationBar->addWidget(m_tabWidget->lineEditStack());
+
+ m_toolbarSearch = new ToolbarSearch(m_navigationBar);
+ m_navigationBar->addWidget(m_toolbarSearch);
+ connect(m_toolbarSearch, SIGNAL(search(const QUrl&)), SLOT(loadUrl(const QUrl&)));
+
+ m_chaseWidget = new ChaseWidget(this);
+ m_navigationBar->addWidget(m_chaseWidget);
+}
+
+void BrowserMainWindow::slotShowBookmarksDialog()
+{
+ BookmarksDialog *dialog = new BookmarksDialog(this);
+ connect(dialog, SIGNAL(openUrl(const QUrl&)),
+ m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&)));
+ dialog->show();
+}
+
+void BrowserMainWindow::slotAddBookmark()
+{
+ WebView *webView = currentTab();
+ QString url = webView->url().toString();
+ QString title = webView->title();
+ AddBookmarkDialog dialog(url, title);
+ dialog.exec();
+}
+
+void BrowserMainWindow::slotViewToolbar()
+{
+ if (m_navigationBar->isVisible()) {
+ updateToolbarActionText(false);
+ m_navigationBar->close();
+ } else {
+ updateToolbarActionText(true);
+ m_navigationBar->show();
+ }
+ m_autoSaver->changeOccurred();
+}
+
+void BrowserMainWindow::slotViewBookmarksBar()
+{
+ if (m_bookmarksToolbar->isVisible()) {
+ updateBookmarksToolbarActionText(false);
+ m_bookmarksToolbar->close();
+ } else {
+ updateBookmarksToolbarActionText(true);
+ m_bookmarksToolbar->show();
+ }
+ m_autoSaver->changeOccurred();
+}
+
+void BrowserMainWindow::updateStatusbarActionText(bool visible)
+{
+ m_viewStatusbar->setText(!visible ? tr("Show Status Bar") : tr("Hide Status Bar"));
+}
+
+void BrowserMainWindow::updateToolbarActionText(bool visible)
+{
+ m_viewToolbar->setText(!visible ? tr("Show Toolbar") : tr("Hide Toolbar"));
+}
+
+void BrowserMainWindow::updateBookmarksToolbarActionText(bool visible)
+{
+ m_viewBookmarkBar->setText(!visible ? tr("Show Bookmarks bar") : tr("Hide Bookmarks bar"));
+}
+
+void BrowserMainWindow::slotViewStatusbar()
+{
+ if (statusBar()->isVisible()) {
+ updateStatusbarActionText(false);
+ statusBar()->close();
+ } else {
+ updateStatusbarActionText(true);
+ statusBar()->show();
+ }
+ m_autoSaver->changeOccurred();
+}
+
+QUrl BrowserMainWindow::guessUrlFromString(const QString &string)
+{
+ QString urlStr = string.trimmed();
+ QRegExp test(QLatin1String("^[a-zA-Z]+\\:.*"));
+
+ // Check if it looks like a qualified URL. Try parsing it and see.
+ bool hasSchema = test.exactMatch(urlStr);
+ if (hasSchema) {
+ QUrl url(urlStr, QUrl::TolerantMode);
+ if (url.isValid())
+ return url;
+ }
+
+ // Might be a file.
+ if (QFile::exists(urlStr)) {
+ QFileInfo info(urlStr);
+ return QUrl::fromLocalFile(info.absoluteFilePath());
+ }
+
+ // Might be a shorturl - try to detect the schema.
+ if (!hasSchema) {
+ int dotIndex = urlStr.indexOf(QLatin1Char('.'));
+ if (dotIndex != -1) {
+ QString prefix = urlStr.left(dotIndex).toLower();
+ QString schema = (prefix == QLatin1String("ftp")) ? prefix : QLatin1String("http");
+ QUrl url(schema + QLatin1String("://") + urlStr, QUrl::TolerantMode);
+ if (url.isValid())
+ return url;
+ }
+ }
+
+ // Fall back to QUrl's own tolerant parser.
+ QUrl url = QUrl(string, QUrl::TolerantMode);
+
+ // finally for cases where the user just types in a hostname add http
+ if (url.scheme().isEmpty())
+ url = QUrl(QLatin1String("http://") + string, QUrl::TolerantMode);
+ return url;
+}
+
+void BrowserMainWindow::loadUrl(const QUrl &url)
+{
+ loadPage(url.toString());
+}
+
+void BrowserMainWindow::slotDownloadManager()
+{
+ BrowserApplication::downloadManager()->show();
+}
+
+void BrowserMainWindow::slotSelectLineEdit()
+{
+ m_tabWidget->currentLineEdit()->selectAll();
+ m_tabWidget->currentLineEdit()->setFocus();
+}
+
+void BrowserMainWindow::slotFileSaveAs()
+{
+ BrowserApplication::downloadManager()->download(currentTab()->url(), true);
+}
+
+void BrowserMainWindow::slotPreferences()
+{
+ SettingsDialog *s = new SettingsDialog(this);
+ s->show();
+}
+
+void BrowserMainWindow::slotUpdateStatusbar(const QString &string)
+{
+ statusBar()->showMessage(string, 2000);
+}
+
+void BrowserMainWindow::slotUpdateWindowTitle(const QString &title)
+{
+ if (title.isEmpty()) {
+ setWindowTitle(tr("Qt Demo Browser"));
+ } else {
+#if defined(Q_WS_MAC)
+ setWindowTitle(title);
+#else
+ setWindowTitle(tr("%1 - Qt Demo Browser", "Page title and Browser name").arg(title));
+#endif
+ }
+}
+
+void BrowserMainWindow::slotAboutApplication()
+{
+ QMessageBox::about(this, tr("About"), tr(
+ "Version %1"
+ "<p>This demo demonstrates Qt's "
+ "webkit facilities in action, providing an example "
+ "browser for you to experiment with.<p>"
+ "<p>QtWebKit is based on the Open Source WebKit Project developed at <a href=\"http://webkit.org/\">http://webkit.org/</a>."
+ ).arg(QCoreApplication::applicationVersion()));
+}
+
+void BrowserMainWindow::slotFileNew()
+{
+ BrowserApplication::instance()->newMainWindow();
+ BrowserMainWindow *mw = BrowserApplication::instance()->mainWindow();
+ mw->slotHome();
+}
+
+void BrowserMainWindow::slotFileOpen()
+{
+ QString file = QFileDialog::getOpenFileName(this, tr("Open Web Resource"), QString(),
+ tr("Web Resources (*.html *.htm *.svg *.png *.gif *.svgz);;All files (*.*)"));
+
+ if (file.isEmpty())
+ return;
+
+ loadPage(file);
+}
+
+void BrowserMainWindow::slotFilePrintPreview()
+{
+ if (!currentTab())
+ return;
+ QPrintPreviewDialog *dialog = new QPrintPreviewDialog(this);
+ connect(dialog, SIGNAL(paintRequested(QPrinter *)),
+ currentTab(), SLOT(print(QPrinter *)));
+ dialog->exec();
+}
+
+void BrowserMainWindow::slotFilePrint()
+{
+ if (!currentTab())
+ return;
+ printRequested(currentTab()->page()->mainFrame());
+}
+
+void BrowserMainWindow::printRequested(QWebFrame *frame)
+{
+ QPrinter printer;
+ QPrintDialog *dialog = new QPrintDialog(&printer, this);
+ dialog->setWindowTitle(tr("Print Document"));
+ if (dialog->exec() != QDialog::Accepted)
+ return;
+ frame->print(&printer);
+}
+
+void BrowserMainWindow::slotPrivateBrowsing()
+{
+ QWebSettings *settings = QWebSettings::globalSettings();
+ bool pb = settings->testAttribute(QWebSettings::PrivateBrowsingEnabled);
+ if (!pb) {
+ QString title = tr("Are you sure you want to turn on private browsing?");
+ QString text = tr("<b>%1</b><br><br>When private browsing in turned on,"
+ " webpages are not added to the history,"
+ " items are automatically removed from the Downloads window," \
+ " new cookies are not stored, current cookies can't be accessed," \
+ " site icons wont be stored, session wont be saved, " \
+ " and searches are not addded to the pop-up menu in the Google search box." \
+ " Until you close the window, you can still click the Back and Forward buttons" \
+ " to return to the webpages you have opened.").arg(title);
+
+ QMessageBox::StandardButton button = QMessageBox::question(this, QString(), text,
+ QMessageBox::Ok | QMessageBox::Cancel,
+ QMessageBox::Ok);
+ if (button == QMessageBox::Ok) {
+ settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, true);
+ }
+ } else {
+ settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false);
+
+ QList<BrowserMainWindow*> windows = BrowserApplication::instance()->mainWindows();
+ for (int i = 0; i < windows.count(); ++i) {
+ BrowserMainWindow *window = windows.at(i);
+ window->m_lastSearch = QString::null;
+ window->tabWidget()->clear();
+ }
+ }
+}
+
+void BrowserMainWindow::closeEvent(QCloseEvent *event)
+{
+ if (m_tabWidget->count() > 1) {
+ int ret = QMessageBox::warning(this, QString(),
+ tr("Are you sure you want to close the window?"
+ " There are %1 tab open").arg(m_tabWidget->count()),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No);
+ if (ret == QMessageBox::No) {
+ event->ignore();
+ return;
+ }
+ }
+ event->accept();
+ deleteLater();
+}
+
+void BrowserMainWindow::slotEditFind()
+{
+ if (!currentTab())
+ return;
+ bool ok;
+ QString search = QInputDialog::getText(this, tr("Find"),
+ tr("Text:"), QLineEdit::Normal,
+ m_lastSearch, &ok);
+ if (ok && !search.isEmpty()) {
+ m_lastSearch = search;
+ if (!currentTab()->findText(m_lastSearch))
+ slotUpdateStatusbar(tr("\"%1\" not found.").arg(m_lastSearch));
+ }
+}
+
+void BrowserMainWindow::slotEditFindNext()
+{
+ if (!currentTab() && !m_lastSearch.isEmpty())
+ return;
+ currentTab()->findText(m_lastSearch);
+}
+
+void BrowserMainWindow::slotEditFindPrevious()
+{
+ if (!currentTab() && !m_lastSearch.isEmpty())
+ return;
+ currentTab()->findText(m_lastSearch, QWebPage::FindBackward);
+}
+
+void BrowserMainWindow::slotViewTextBigger()
+{
+ if (!currentTab())
+ return;
+ currentTab()->setTextSizeMultiplier(currentTab()->textSizeMultiplier() + 0.1);
+}
+
+void BrowserMainWindow::slotViewTextNormal()
+{
+ if (!currentTab())
+ return;
+ currentTab()->setTextSizeMultiplier(1.0);
+}
+
+void BrowserMainWindow::slotViewTextSmaller()
+{
+ if (!currentTab())
+ return;
+ currentTab()->setTextSizeMultiplier(currentTab()->textSizeMultiplier() - 0.1);
+}
+
+void BrowserMainWindow::slotViewFullScreen(bool makeFullScreen)
+{
+ if (makeFullScreen) {
+ showFullScreen();
+ } else {
+ if (isMinimized())
+ showMinimized();
+ else if (isMaximized())
+ showMaximized();
+ else showNormal();
+ }
+}
+
+void BrowserMainWindow::slotViewPageSource()
+{
+ if (!currentTab())
+ return;
+
+ QString markup = currentTab()->page()->mainFrame()->toHtml();
+ QPlainTextEdit *view = new QPlainTextEdit(markup);
+ view->setWindowTitle(tr("Page Source of %1").arg(currentTab()->title()));
+ view->setMinimumWidth(640);
+ view->setAttribute(Qt::WA_DeleteOnClose);
+ view->show();
+}
+
+void BrowserMainWindow::slotHome()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("MainWindow"));
+ QString home = settings.value(QLatin1String("home"), QLatin1String("http://www.trolltech.com/")).toString();
+ loadPage(home);
+}
+
+void BrowserMainWindow::slotWebSearch()
+{
+ m_toolbarSearch->lineEdit()->selectAll();
+ m_toolbarSearch->lineEdit()->setFocus();
+}
+
+void BrowserMainWindow::slotToggleInspector(bool enable)
+{
+ QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enable);
+ if (enable) {
+ int result = QMessageBox::question(this, tr("Web Inspector"),
+ tr("The web inspector will only work correctly for pages that were loaded after enabling.\n"
+ "Do you want to reload all pages?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (result == QMessageBox::Yes) {
+ m_tabWidget->reloadAllTabs();
+ }
+ }
+}
+
+void BrowserMainWindow::slotSwapFocus()
+{
+ if (currentTab()->hasFocus())
+ m_tabWidget->currentLineEdit()->setFocus();
+ else
+ currentTab()->setFocus();
+}
+
+void BrowserMainWindow::loadPage(const QString &page)
+{
+ if (!currentTab() || page.isEmpty())
+ return;
+
+ QUrl url = guessUrlFromString(page);
+ m_tabWidget->currentLineEdit()->setText(url.toString());
+ m_tabWidget->loadUrlInCurrentTab(url);
+}
+
+TabWidget *BrowserMainWindow::tabWidget() const
+{
+ return m_tabWidget;
+}
+
+WebView *BrowserMainWindow::currentTab() const
+{
+ return m_tabWidget->currentWebView();
+}
+
+void BrowserMainWindow::slotLoadProgress(int progress)
+{
+ if (progress < 100 && progress > 0) {
+ m_chaseWidget->setAnimated(true);
+ disconnect(m_stopReload, SIGNAL(triggered()), m_reload, SLOT(trigger()));
+ if (m_stopIcon.isNull())
+ m_stopIcon = style()->standardIcon(QStyle::SP_BrowserStop);
+ m_stopReload->setIcon(m_stopIcon);
+ connect(m_stopReload, SIGNAL(triggered()), m_stop, SLOT(trigger()));
+ m_stopReload->setToolTip(tr("Stop loading the current page"));
+ } else {
+ m_chaseWidget->setAnimated(false);
+ disconnect(m_stopReload, SIGNAL(triggered()), m_stop, SLOT(trigger()));
+ m_stopReload->setIcon(m_reloadIcon);
+ connect(m_stopReload, SIGNAL(triggered()), m_reload, SLOT(trigger()));
+ m_stopReload->setToolTip(tr("Reload the current page"));
+ }
+}
+
+void BrowserMainWindow::slotAboutToShowBackMenu()
+{
+ m_historyBackMenu->clear();
+ if (!currentTab())
+ return;
+ QWebHistory *history = currentTab()->history();
+ int historyCount = history->count();
+ for (int i = history->backItems(historyCount).count() - 1; i >= 0; --i) {
+ QWebHistoryItem item = history->backItems(history->count()).at(i);
+ QAction *action = new QAction(this);
+ action->setData(-1*(historyCount-i-1));
+ QIcon icon = BrowserApplication::instance()->icon(item.url());
+ action->setIcon(icon);
+ action->setText(item.title());
+ m_historyBackMenu->addAction(action);
+ }
+}
+
+void BrowserMainWindow::slotAboutToShowForwardMenu()
+{
+ m_historyForwardMenu->clear();
+ if (!currentTab())
+ return;
+ QWebHistory *history = currentTab()->history();
+ int historyCount = history->count();
+ for (int i = 0; i < history->forwardItems(history->count()).count(); ++i) {
+ QWebHistoryItem item = history->forwardItems(historyCount).at(i);
+ QAction *action = new QAction(this);
+ action->setData(historyCount-i);
+ QIcon icon = BrowserApplication::instance()->icon(item.url());
+ action->setIcon(icon);
+ action->setText(item.title());
+ m_historyForwardMenu->addAction(action);
+ }
+}
+
+void BrowserMainWindow::slotAboutToShowWindowMenu()
+{
+ m_windowMenu->clear();
+ m_windowMenu->addAction(m_tabWidget->nextTabAction());
+ m_windowMenu->addAction(m_tabWidget->previousTabAction());
+ m_windowMenu->addSeparator();
+ m_windowMenu->addAction(tr("Downloads"), this, SLOT(slotDownloadManager()), QKeySequence(tr("Alt+Ctrl+L", "Download Manager")));
+
+ m_windowMenu->addSeparator();
+ QList<BrowserMainWindow*> windows = BrowserApplication::instance()->mainWindows();
+ for (int i = 0; i < windows.count(); ++i) {
+ BrowserMainWindow *window = windows.at(i);
+ QAction *action = m_windowMenu->addAction(window->windowTitle(), this, SLOT(slotShowWindow()));
+ action->setData(i);
+ action->setCheckable(true);
+ if (window == this)
+ action->setChecked(true);
+ }
+}
+
+void BrowserMainWindow::slotShowWindow()
+{
+ if (QAction *action = qobject_cast<QAction*>(sender())) {
+ QVariant v = action->data();
+ if (v.canConvert<int>()) {
+ int offset = qvariant_cast<int>(v);
+ QList<BrowserMainWindow*> windows = BrowserApplication::instance()->mainWindows();
+ windows.at(offset)->activateWindow();
+ windows.at(offset)->currentTab()->setFocus();
+ }
+ }
+}
+
+void BrowserMainWindow::slotOpenActionUrl(QAction *action)
+{
+ int offset = action->data().toInt();
+ QWebHistory *history = currentTab()->history();
+ if (offset < 0)
+ history->goToItem(history->backItems(-1*offset).first()); // back
+ else if (offset > 0)
+ history->goToItem(history->forwardItems(history->count() - offset + 1).back()); // forward
+ }
+
+void BrowserMainWindow::geometryChangeRequested(const QRect &geometry)
+{
+ setGeometry(geometry);
+}
diff --git a/src/browsermainwindow.h b/src/browsermainwindow.h
new file mode 100644
index 00000000..17f87056
--- /dev/null
+++ b/src/browsermainwindow.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef BROWSERMAINWINDOW_H
+#define BROWSERMAINWINDOW_H
+
+#include <QtGui/QMainWindow>
+#include <QtGui/QIcon>
+#include <QtCore/QUrl>
+
+class AutoSaver;
+class BookmarksToolBar;
+class ChaseWidget;
+class QWebFrame;
+class TabWidget;
+class ToolbarSearch;
+class WebView;
+
+/*!
+ The MainWindow of the Browser Application.
+
+ Handles the tab widget and all the actions
+ */
+class BrowserMainWindow : public QMainWindow {
+ Q_OBJECT
+
+public:
+ BrowserMainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
+ ~BrowserMainWindow();
+ QSize sizeHint() const;
+
+public:
+ static QUrl guessUrlFromString(const QString &url);
+ TabWidget *tabWidget() const;
+ WebView *currentTab() const;
+ QByteArray saveState(bool withTabs = true) const;
+ bool restoreState(const QByteArray &state);
+
+public slots:
+ void loadPage(const QString &url);
+ void slotHome();
+
+protected:
+ void closeEvent(QCloseEvent *event);
+
+private slots:
+ void save();
+
+ void slotLoadProgress(int);
+ void slotUpdateStatusbar(const QString &string);
+ void slotUpdateWindowTitle(const QString &title = QString());
+
+ void loadUrl(const QUrl &url);
+ void slotPreferences();
+
+ void slotFileNew();
+ void slotFileOpen();
+ void slotFilePrintPreview();
+ void slotFilePrint();
+ void slotPrivateBrowsing();
+ void slotFileSaveAs();
+ void slotEditFind();
+ void slotEditFindNext();
+ void slotEditFindPrevious();
+ void slotShowBookmarksDialog();
+ void slotAddBookmark();
+ void slotViewTextBigger();
+ void slotViewTextNormal();
+ void slotViewTextSmaller();
+ void slotViewToolbar();
+ void slotViewBookmarksBar();
+ void slotViewStatusbar();
+ void slotViewPageSource();
+ void slotViewFullScreen(bool enable);
+
+ void slotWebSearch();
+ void slotToggleInspector(bool enable);
+ void slotAboutApplication();
+ void slotDownloadManager();
+ void slotSelectLineEdit();
+
+ void slotAboutToShowBackMenu();
+ void slotAboutToShowForwardMenu();
+ void slotAboutToShowWindowMenu();
+ void slotOpenActionUrl(QAction *action);
+ void slotShowWindow();
+ void slotSwapFocus();
+
+ void printRequested(QWebFrame *frame);
+ void geometryChangeRequested(const QRect &geometry);
+ void updateToolbarActionText(bool visible);
+ void updateBookmarksToolbarActionText(bool visible);
+
+private:
+ void loadDefaultState();
+ void setupMenu();
+ void setupToolBar();
+ void updateStatusbarActionText(bool visible);
+
+private:
+ QToolBar *m_navigationBar;
+ ToolbarSearch *m_toolbarSearch;
+ BookmarksToolBar *m_bookmarksToolbar;
+ ChaseWidget *m_chaseWidget;
+ TabWidget *m_tabWidget;
+ AutoSaver *m_autoSaver;
+
+ QAction *m_historyBack;
+ QMenu *m_historyBackMenu;
+ QAction *m_historyForward;
+ QMenu *m_historyForwardMenu;
+ QMenu *m_windowMenu;
+
+ QAction *m_stop;
+ QAction *m_reload;
+ QAction *m_stopReload;
+ QAction *m_viewToolbar;
+ QAction *m_viewBookmarkBar;
+ QAction *m_viewStatusbar;
+ QAction *m_restoreLastSession;
+ QAction *m_addBookmark;
+
+ QIcon m_reloadIcon;
+ QIcon m_stopIcon;
+
+ QString m_lastSearch;
+};
+
+#endif // BROWSERMAINWINDOW_H
+
diff --git a/src/chasewidget.cpp b/src/chasewidget.cpp
new file mode 100644
index 00000000..ea2b0f8e
--- /dev/null
+++ b/src/chasewidget.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "chasewidget.h"
+
+#include <QtCore/QPoint>
+
+#include <QtGui/QApplication>
+#include <QtGui/QHideEvent>
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEvent>
+#include <QtGui/QShowEvent>
+
+ChaseWidget::ChaseWidget(QWidget *parent, QPixmap pixmap, bool pixmapEnabled)
+ : QWidget(parent)
+ , m_segment(0)
+ , m_delay(100)
+ , m_step(40)
+ , m_timerId(-1)
+ , m_animated(false)
+ , m_pixmap(pixmap)
+ , m_pixmapEnabled(pixmapEnabled)
+{
+}
+
+void ChaseWidget::setAnimated(bool value)
+{
+ if (m_animated == value)
+ return;
+ m_animated = value;
+ if (m_timerId != -1) {
+ killTimer(m_timerId);
+ m_timerId = -1;
+ }
+ if (m_animated) {
+ m_segment = 0;
+ m_timerId = startTimer(m_delay);
+ }
+ update();
+}
+
+void ChaseWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ QPainter p(this);
+ if (m_pixmapEnabled && !m_pixmap.isNull()) {
+ p.drawPixmap(0, 0, m_pixmap);
+ return;
+ }
+
+ const int extent = qMin(width() - 8, height() - 8);
+ const int displ = extent / 4;
+ const int ext = extent / 4 - 1;
+
+ p.setRenderHint(QPainter::Antialiasing, true);
+
+ if(m_animated)
+ p.setPen(Qt::gray);
+ else
+ p.setPen(QPen(palette().dark().color()));
+
+ p.translate(width() / 2, height() / 2); // center
+
+ for (int segment = 0; segment < segmentCount(); ++segment) {
+ p.rotate(QApplication::isRightToLeft() ? m_step : -m_step);
+ if(m_animated)
+ p.setBrush(colorForSegment(segment));
+ else
+ p.setBrush(palette().background());
+ p.drawEllipse(QRect(displ, -ext / 2, ext, ext));
+ }
+}
+
+QSize ChaseWidget::sizeHint() const
+{
+ return QSize(32, 32);
+}
+
+void ChaseWidget::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_timerId) {
+ ++m_segment;
+ update();
+ }
+ QWidget::timerEvent(event);
+}
+
+QColor ChaseWidget::colorForSegment(int seg) const
+{
+ int index = ((seg + m_segment) % segmentCount());
+ int comp = qMax(0, 255 - (index * (255 / segmentCount())));
+ return QColor(comp, comp, comp, 255);
+}
+
+int ChaseWidget::segmentCount() const
+{
+ return 360 / m_step;
+}
+
+void ChaseWidget::setPixmapEnabled(bool enable)
+{
+ m_pixmapEnabled = enable;
+}
+
diff --git a/src/chasewidget.h b/src/chasewidget.h
new file mode 100644
index 00000000..ce5b2dae
--- /dev/null
+++ b/src/chasewidget.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef CHASEWIDGET_H
+#define CHASEWIDGET_H
+
+#include <QtGui/QWidget>
+
+#include <QtCore/QSize>
+#include <QtGui/QColor>
+#include <QtGui/QPixmap>
+
+QT_BEGIN_NAMESPACE
+class QHideEvent;
+class QShowEvent;
+class QPaintEvent;
+class QTimerEvent;
+QT_END_NAMESPACE
+
+class ChaseWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ ChaseWidget(QWidget *parent = 0, QPixmap pixmap = QPixmap(), bool pixmapEnabled = false);
+
+ void setAnimated(bool value);
+ void setPixmapEnabled(bool enable);
+ QSize sizeHint() const;
+
+protected:
+ void paintEvent(QPaintEvent *event);
+ void timerEvent(QTimerEvent *event);
+
+private:
+ int segmentCount() const;
+ QColor colorForSegment(int segment) const;
+
+ int m_segment;
+ int m_delay;
+ int m_step;
+ int m_timerId;
+ bool m_animated;
+ QPixmap m_pixmap;
+ bool m_pixmapEnabled;
+};
+
+#endif
diff --git a/src/cookiejar.cpp b/src/cookiejar.cpp
new file mode 100644
index 00000000..bed7258a
--- /dev/null
+++ b/src/cookiejar.cpp
@@ -0,0 +1,728 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "cookiejar.h"
+
+#include "autosaver.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QMetaEnum>
+#include <QtCore/QSettings>
+#include <QtCore/QUrl>
+
+#include <QtGui/QCompleter>
+#include <QtGui/QDesktopServices>
+#include <QtGui/QFont>
+#include <QtGui/QFontMetrics>
+#include <QtGui/QHeaderView>
+#include <QtGui/QKeyEvent>
+#include <QtGui/QSortFilterProxyModel>
+
+#include <QtWebKit/QWebSettings>
+
+#include <QtCore/QDebug>
+
+static const unsigned int JAR_VERSION = 23;
+
+QT_BEGIN_NAMESPACE
+QDataStream &operator<<(QDataStream &stream, const QList<QNetworkCookie> &list)
+{
+ stream << JAR_VERSION;
+ stream << quint32(list.size());
+ for (int i = 0; i < list.size(); ++i)
+ stream << list.at(i).toRawForm();
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QList<QNetworkCookie> &list)
+{
+ list.clear();
+
+ quint32 version;
+ stream >> version;
+
+ if (version != JAR_VERSION)
+ return stream;
+
+ quint32 count;
+ stream >> count;
+ for(quint32 i = 0; i < count; ++i)
+ {
+ QByteArray value;
+ stream >> value;
+ QList<QNetworkCookie> newCookies = QNetworkCookie::parseCookies(value);
+ if (newCookies.count() == 0 && value.length() != 0) {
+ qWarning() << "CookieJar: Unable to parse saved cookie:" << value;
+ }
+ for (int j = 0; j < newCookies.count(); ++j)
+ list.append(newCookies.at(j));
+ if (stream.atEnd())
+ break;
+ }
+ return stream;
+}
+QT_END_NAMESPACE
+
+CookieJar::CookieJar(QObject *parent)
+ : QNetworkCookieJar(parent)
+ , m_loaded(false)
+ , m_saveTimer(new AutoSaver(this))
+ , m_acceptCookies(AcceptOnlyFromSitesNavigatedTo)
+{
+}
+
+CookieJar::~CookieJar()
+{
+ if (m_keepCookies == KeepUntilExit)
+ clear();
+ m_saveTimer->saveIfNeccessary();
+}
+
+void CookieJar::clear()
+{
+ setAllCookies(QList<QNetworkCookie>());
+ m_saveTimer->changeOccurred();
+ emit cookiesChanged();
+}
+
+void CookieJar::load()
+{
+ if (m_loaded)
+ return;
+ // load cookies and exceptions
+ qRegisterMetaTypeStreamOperators<QList<QNetworkCookie> >("QList<QNetworkCookie>");
+ QSettings cookieSettings(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/cookies.ini"), QSettings::IniFormat);
+ setAllCookies(qvariant_cast<QList<QNetworkCookie> >(cookieSettings.value(QLatin1String("cookies"))));
+ cookieSettings.beginGroup(QLatin1String("Exceptions"));
+ m_exceptions_block = cookieSettings.value(QLatin1String("block")).toStringList();
+ m_exceptions_allow = cookieSettings.value(QLatin1String("allow")).toStringList();
+ m_exceptions_allowForSession = cookieSettings.value(QLatin1String("allowForSession")).toStringList();
+ qSort(m_exceptions_block.begin(), m_exceptions_block.end());
+ qSort(m_exceptions_allow.begin(), m_exceptions_allow.end());
+ qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end());
+
+ loadSettings();
+}
+
+void CookieJar::loadSettings()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("cookies"));
+ QByteArray value = settings.value(QLatin1String("acceptCookies"),
+ QLatin1String("AcceptOnlyFromSitesNavigatedTo")).toByteArray();
+ QMetaEnum acceptPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AcceptPolicy"));
+ m_acceptCookies = acceptPolicyEnum.keyToValue(value) == -1 ?
+ AcceptOnlyFromSitesNavigatedTo :
+ static_cast<AcceptPolicy>(acceptPolicyEnum.keyToValue(value));
+
+ value = settings.value(QLatin1String("keepCookiesUntil"), QLatin1String("KeepUntilExpire")).toByteArray();
+ QMetaEnum keepPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("KeepPolicy"));
+ m_keepCookies = keepPolicyEnum.keyToValue(value) == -1 ?
+ KeepUntilExpire :
+ static_cast<KeepPolicy>(keepPolicyEnum.keyToValue(value));
+
+ if (m_keepCookies == KeepUntilExit)
+ setAllCookies(QList<QNetworkCookie>());
+
+ m_loaded = true;
+ emit cookiesChanged();
+}
+
+void CookieJar::save()
+{
+ if (!m_loaded)
+ return;
+ purgeOldCookies();
+ QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ if (directory.isEmpty())
+ directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName();
+ if (!QFile::exists(directory)) {
+ QDir dir;
+ dir.mkpath(directory);
+ }
+ QSettings cookieSettings(directory + QLatin1String("/cookies.ini"), QSettings::IniFormat);
+ QList<QNetworkCookie> cookies = allCookies();
+ for (int i = cookies.count() - 1; i >= 0; --i) {
+ if (cookies.at(i).isSessionCookie())
+ cookies.removeAt(i);
+ }
+ cookieSettings.setValue(QLatin1String("cookies"), qVariantFromValue<QList<QNetworkCookie> >(cookies));
+ cookieSettings.beginGroup(QLatin1String("Exceptions"));
+ cookieSettings.setValue(QLatin1String("block"), m_exceptions_block);
+ cookieSettings.setValue(QLatin1String("allow"), m_exceptions_allow);
+ cookieSettings.setValue(QLatin1String("allowForSession"), m_exceptions_allowForSession);
+
+ // save cookie settings
+ QSettings settings;
+ settings.beginGroup(QLatin1String("cookies"));
+ QMetaEnum acceptPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AcceptPolicy"));
+ settings.setValue(QLatin1String("acceptCookies"), QLatin1String(acceptPolicyEnum.valueToKey(m_acceptCookies)));
+
+ QMetaEnum keepPolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("KeepPolicy"));
+ settings.setValue(QLatin1String("keepCookiesUntil"), QLatin1String(keepPolicyEnum.valueToKey(m_keepCookies)));
+}
+
+void CookieJar::purgeOldCookies()
+{
+ QList<QNetworkCookie> cookies = allCookies();
+ if (cookies.isEmpty())
+ return;
+ int oldCount = cookies.count();
+ QDateTime now = QDateTime::currentDateTime();
+ for (int i = cookies.count() - 1; i >= 0; --i) {
+ if (!cookies.at(i).isSessionCookie() && cookies.at(i).expirationDate() < now)
+ cookies.removeAt(i);
+ }
+ if (oldCount == cookies.count())
+ return;
+ setAllCookies(cookies);
+ emit cookiesChanged();
+}
+
+QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const
+{
+ CookieJar *that = const_cast<CookieJar*>(this);
+ if (!m_loaded)
+ that->load();
+
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) {
+ QList<QNetworkCookie> noCookies;
+ return noCookies;
+ }
+
+ return QNetworkCookieJar::cookiesForUrl(url);
+}
+
+bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
+{
+ if (!m_loaded)
+ load();
+
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
+ return false;
+
+ QString host = url.host();
+ bool eBlock = qBinaryFind(m_exceptions_block.begin(), m_exceptions_block.end(), host) != m_exceptions_block.end();
+ bool eAllow = qBinaryFind(m_exceptions_allow.begin(), m_exceptions_allow.end(), host) != m_exceptions_allow.end();
+ bool eAllowSession = qBinaryFind(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end(), host) != m_exceptions_allowForSession.end();
+
+ bool addedCookies = false;
+ // pass exceptions
+ bool acceptInitially = (m_acceptCookies != AcceptNever);
+ if ((acceptInitially && !eBlock)
+ || (!acceptInitially && (eAllow || eAllowSession))) {
+ // pass url domain == cookie domain
+ QDateTime soon = QDateTime::currentDateTime();
+ soon = soon.addDays(90);
+ foreach(QNetworkCookie cookie, cookieList) {
+ QList<QNetworkCookie> lst;
+ if (m_keepCookies == KeepUntilTimeLimit
+ && !cookie.isSessionCookie()
+ && cookie.expirationDate() > soon) {
+ cookie.setExpirationDate(soon);
+ }
+ lst += cookie;
+ if (QNetworkCookieJar::setCookiesFromUrl(lst, url)) {
+ addedCookies = true;
+ } else {
+ // finally force it in if wanted
+ if (m_acceptCookies == AcceptAlways) {
+ QList<QNetworkCookie> cookies = allCookies();
+ cookies += cookie;
+ setAllCookies(cookies);
+ addedCookies = true;
+ }
+#if 0
+ else
+ qWarning() << "setCookiesFromUrl failed" << url << cookieList.value(0).toRawForm();
+#endif
+ }
+ }
+ }
+
+ if (addedCookies) {
+ m_saveTimer->changeOccurred();
+ emit cookiesChanged();
+ }
+ return addedCookies;
+}
+
+CookieJar::AcceptPolicy CookieJar::acceptPolicy() const
+{
+ if (!m_loaded)
+ (const_cast<CookieJar*>(this))->load();
+ return m_acceptCookies;
+}
+
+void CookieJar::setAcceptPolicy(AcceptPolicy policy)
+{
+ if (!m_loaded)
+ load();
+ if (policy == m_acceptCookies)
+ return;
+ m_acceptCookies = policy;
+ m_saveTimer->changeOccurred();
+}
+
+CookieJar::KeepPolicy CookieJar::keepPolicy() const
+{
+ if (!m_loaded)
+ (const_cast<CookieJar*>(this))->load();
+ return m_keepCookies;
+}
+
+void CookieJar::setKeepPolicy(KeepPolicy policy)
+{
+ if (!m_loaded)
+ load();
+ if (policy == m_keepCookies)
+ return;
+ m_keepCookies = policy;
+ m_saveTimer->changeOccurred();
+}
+
+QStringList CookieJar::blockedCookies() const
+{
+ if (!m_loaded)
+ (const_cast<CookieJar*>(this))->load();
+ return m_exceptions_block;
+}
+
+QStringList CookieJar::allowedCookies() const
+{
+ if (!m_loaded)
+ (const_cast<CookieJar*>(this))->load();
+ return m_exceptions_allow;
+}
+
+QStringList CookieJar::allowForSessionCookies() const
+{
+ if (!m_loaded)
+ (const_cast<CookieJar*>(this))->load();
+ return m_exceptions_allowForSession;
+}
+
+void CookieJar::setBlockedCookies(const QStringList &list)
+{
+ if (!m_loaded)
+ load();
+ m_exceptions_block = list;
+ qSort(m_exceptions_block.begin(), m_exceptions_block.end());
+ m_saveTimer->changeOccurred();
+}
+
+void CookieJar::setAllowedCookies(const QStringList &list)
+{
+ if (!m_loaded)
+ load();
+ m_exceptions_allow = list;
+ qSort(m_exceptions_allow.begin(), m_exceptions_allow.end());
+ m_saveTimer->changeOccurred();
+}
+
+void CookieJar::setAllowForSessionCookies(const QStringList &list)
+{
+ if (!m_loaded)
+ load();
+ m_exceptions_allowForSession = list;
+ qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end());
+ m_saveTimer->changeOccurred();
+}
+
+CookieModel::CookieModel(CookieJar *cookieJar, QObject *parent)
+ : QAbstractTableModel(parent)
+ , m_cookieJar(cookieJar)
+{
+ connect(m_cookieJar, SIGNAL(cookiesChanged()), this, SLOT(cookiesChanged()));
+ m_cookieJar->load();
+}
+
+QVariant CookieModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role == Qt::SizeHintRole) {
+ QFont font;
+ font.setPointSize(10);
+ QFontMetrics fm(font);
+ int height = fm.height() + fm.height()/3;
+ int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString());
+ return QSize(width, height);
+ }
+
+ if (orientation == Qt::Horizontal) {
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ switch (section) {
+ case 0:
+ return tr("Website");
+ case 1:
+ return tr("Name");
+ case 2:
+ return tr("Path");
+ case 3:
+ return tr("Secure");
+ case 4:
+ return tr("Expires");
+ case 5:
+ return tr("Contents");
+ default:
+ return QVariant();
+ }
+ }
+ return QAbstractTableModel::headerData(section, orientation, role);
+}
+
+QVariant CookieModel::data(const QModelIndex &index, int role) const
+{
+ QList<QNetworkCookie> lst;
+ if (m_cookieJar)
+ lst = m_cookieJar->allCookies();
+ if (index.row() < 0 || index.row() >= lst.size())
+ return QVariant();
+
+ switch (role) {
+ case Qt::DisplayRole:
+ case Qt::EditRole: {
+ QNetworkCookie cookie = lst.at(index.row());
+ switch (index.column()) {
+ case 0:
+ return cookie.domain();
+ case 1:
+ return cookie.name();
+ case 2:
+ return cookie.path();
+ case 3:
+ return cookie.isSecure();
+ case 4:
+ return cookie.expirationDate();
+ case 5:
+ return cookie.value();
+ }
+ }
+ case Qt::FontRole:{
+ QFont font;
+ font.setPointSize(10);
+ return font;
+ }
+ }
+
+ return QVariant();
+}
+
+int CookieModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : 6;
+}
+
+int CookieModel::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid() || !m_cookieJar) ? 0 : m_cookieJar->allCookies().count();
+}
+
+bool CookieModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (parent.isValid() || !m_cookieJar)
+ return false;
+ int lastRow = row + count - 1;
+ beginRemoveRows(parent, row, lastRow);
+ QList<QNetworkCookie> lst = m_cookieJar->allCookies();
+ for (int i = lastRow; i >= row; --i) {
+ lst.removeAt(i);
+ }
+ m_cookieJar->setAllCookies(lst);
+ endRemoveRows();
+ return true;
+}
+
+void CookieModel::cookiesChanged()
+{
+ reset();
+}
+
+CookiesDialog::CookiesDialog(CookieJar *cookieJar, QWidget *parent) : QDialog(parent)
+{
+ setupUi(this);
+ setWindowFlags(Qt::Sheet);
+ CookieModel *model = new CookieModel(cookieJar, this);
+ m_proxyModel = new QSortFilterProxyModel(this);
+ connect(search, SIGNAL(textChanged(QString)),
+ m_proxyModel, SLOT(setFilterFixedString(QString)));
+ connect(removeButton, SIGNAL(clicked()), cookiesTable, SLOT(removeOne()));
+ connect(removeAllButton, SIGNAL(clicked()), cookiesTable, SLOT(removeAll()));
+ m_proxyModel->setSourceModel(model);
+ cookiesTable->verticalHeader()->hide();
+ cookiesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ cookiesTable->setModel(m_proxyModel);
+ cookiesTable->setAlternatingRowColors(true);
+ cookiesTable->setTextElideMode(Qt::ElideMiddle);
+ cookiesTable->setShowGrid(false);
+ cookiesTable->setSortingEnabled(true);
+ QFont f = font();
+ f.setPointSize(10);
+ QFontMetrics fm(f);
+ int height = fm.height() + fm.height()/3;
+ cookiesTable->verticalHeader()->setDefaultSectionSize(height);
+ cookiesTable->verticalHeader()->setMinimumSectionSize(-1);
+ for (int i = 0; i < model->columnCount(); ++i){
+ int header = cookiesTable->horizontalHeader()->sectionSizeHint(i);
+ switch (i) {
+ case 0:
+ header = fm.width(QLatin1String("averagehost.domain.com"));
+ break;
+ case 1:
+ header = fm.width(QLatin1String("_session_id"));
+ break;
+ case 4:
+ header = fm.width(QDateTime::currentDateTime().toString(Qt::LocalDate));
+ break;
+ }
+ int buffer = fm.width(QLatin1String("xx"));
+ header += buffer;
+ cookiesTable->horizontalHeader()->resizeSection(i, header);
+ }
+ cookiesTable->horizontalHeader()->setStretchLastSection(true);
+}
+
+
+
+CookieExceptionsModel::CookieExceptionsModel(CookieJar *cookiejar, QObject *parent)
+ : QAbstractTableModel(parent)
+ , m_cookieJar(cookiejar)
+{
+ m_allowedCookies = m_cookieJar->allowedCookies();
+ m_blockedCookies = m_cookieJar->blockedCookies();
+ m_sessionCookies = m_cookieJar->allowForSessionCookies();
+}
+
+QVariant CookieExceptionsModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role == Qt::SizeHintRole) {
+ QFont font;
+ font.setPointSize(10);
+ QFontMetrics fm(font);
+ int height = fm.height() + fm.height()/3;
+ int width = fm.width(headerData(section, orientation, Qt::DisplayRole).toString());
+ return QSize(width, height);
+ }
+
+ if (orientation == Qt::Horizontal
+ && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0:
+ return tr("Website");
+ case 1:
+ return tr("Status");
+ }
+ }
+ return QAbstractTableModel::headerData(section, orientation, role);
+}
+
+QVariant CookieExceptionsModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() >= rowCount())
+ return QVariant();
+
+ switch (role) {
+ case Qt::DisplayRole:
+ case Qt::EditRole: {
+ int row = index.row();
+ if (row < m_allowedCookies.count()) {
+ switch (index.column()) {
+ case 0:
+ return m_allowedCookies.at(row);
+ case 1:
+ return tr("Allow");
+ }
+ }
+ row = row - m_allowedCookies.count();
+ if (row < m_blockedCookies.count()) {
+ switch (index.column()) {
+ case 0:
+ return m_blockedCookies.at(row);
+ case 1:
+ return tr("Block");
+ }
+ }
+ row = row - m_blockedCookies.count();
+ if (row < m_sessionCookies.count()) {
+ switch (index.column()) {
+ case 0:
+ return m_sessionCookies.at(row);
+ case 1:
+ return tr("Allow For Session");
+ }
+ }
+ }
+ case Qt::FontRole:{
+ QFont font;
+ font.setPointSize(10);
+ return font;
+ }
+ }
+ return QVariant();
+}
+
+int CookieExceptionsModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : 2;
+}
+
+int CookieExceptionsModel::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid() || !m_cookieJar) ? 0 : m_allowedCookies.count() + m_blockedCookies.count() + m_sessionCookies.count();
+}
+
+bool CookieExceptionsModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (parent.isValid() || !m_cookieJar)
+ return false;
+
+ int lastRow = row + count - 1;
+ beginRemoveRows(parent, row, lastRow);
+ for (int i = lastRow; i >= row; --i) {
+ if (i < m_allowedCookies.count()) {
+ m_allowedCookies.removeAt(row);
+ continue;
+ }
+ i = i - m_allowedCookies.count();
+ if (i < m_blockedCookies.count()) {
+ m_blockedCookies.removeAt(row);
+ continue;
+ }
+ i = i - m_blockedCookies.count();
+ if (i < m_sessionCookies.count()) {
+ m_sessionCookies.removeAt(row);
+ continue;
+ }
+ }
+ m_cookieJar->setAllowedCookies(m_allowedCookies);
+ m_cookieJar->setBlockedCookies(m_blockedCookies);
+ m_cookieJar->setAllowForSessionCookies(m_sessionCookies);
+ endRemoveRows();
+ return true;
+}
+
+CookiesExceptionsDialog::CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent)
+ : QDialog(parent)
+ , m_cookieJar(cookieJar)
+{
+ setupUi(this);
+ setWindowFlags(Qt::Sheet);
+ connect(removeButton, SIGNAL(clicked()), exceptionTable, SLOT(removeOne()));
+ connect(removeAllButton, SIGNAL(clicked()), exceptionTable, SLOT(removeAll()));
+ exceptionTable->verticalHeader()->hide();
+ exceptionTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ exceptionTable->setAlternatingRowColors(true);
+ exceptionTable->setTextElideMode(Qt::ElideMiddle);
+ exceptionTable->setShowGrid(false);
+ exceptionTable->setSortingEnabled(true);
+ m_exceptionsModel = new CookieExceptionsModel(cookieJar, this);
+ m_proxyModel = new QSortFilterProxyModel(this);
+ m_proxyModel->setSourceModel(m_exceptionsModel);
+ connect(search, SIGNAL(textChanged(QString)),
+ m_proxyModel, SLOT(setFilterFixedString(QString)));
+ exceptionTable->setModel(m_proxyModel);
+
+ CookieModel *cookieModel = new CookieModel(cookieJar, this);
+ domainLineEdit->setCompleter(new QCompleter(cookieModel, domainLineEdit));
+
+ connect(domainLineEdit, SIGNAL(textChanged(const QString &)),
+ this, SLOT(textChanged(const QString &)));
+ connect(blockButton, SIGNAL(clicked()), this, SLOT(block()));
+ connect(allowButton, SIGNAL(clicked()), this, SLOT(allow()));
+ connect(allowForSessionButton, SIGNAL(clicked()), this, SLOT(allowForSession()));
+
+ QFont f = font();
+ f.setPointSize(10);
+ QFontMetrics fm(f);
+ int height = fm.height() + fm.height()/3;
+ exceptionTable->verticalHeader()->setDefaultSectionSize(height);
+ exceptionTable->verticalHeader()->setMinimumSectionSize(-1);
+ for (int i = 0; i < m_exceptionsModel->columnCount(); ++i){
+ int header = exceptionTable->horizontalHeader()->sectionSizeHint(i);
+ switch (i) {
+ case 0:
+ header = fm.width(QLatin1String("averagebiglonghost.domain.com"));
+ break;
+ case 1:
+ header = fm.width(QLatin1String("Allow For Session"));
+ break;
+ }
+ int buffer = fm.width(QLatin1String("xx"));
+ header += buffer;
+ exceptionTable->horizontalHeader()->resizeSection(i, header);
+ }
+}
+
+void CookiesExceptionsDialog::textChanged(const QString &text)
+{
+ bool enabled = !text.isEmpty();
+ blockButton->setEnabled(enabled);
+ allowButton->setEnabled(enabled);
+ allowForSessionButton->setEnabled(enabled);
+}
+
+void CookiesExceptionsDialog::block()
+{
+ if (domainLineEdit->text().isEmpty())
+ return;
+ m_exceptionsModel->m_blockedCookies.append(domainLineEdit->text());
+ m_cookieJar->setBlockedCookies(m_exceptionsModel->m_blockedCookies);
+ m_exceptionsModel->reset();
+}
+
+void CookiesExceptionsDialog::allow()
+{
+ if (domainLineEdit->text().isEmpty())
+ return;
+ m_exceptionsModel->m_allowedCookies.append(domainLineEdit->text());
+ m_cookieJar->setAllowedCookies(m_exceptionsModel->m_allowedCookies);
+ m_exceptionsModel->reset();
+}
+
+void CookiesExceptionsDialog::allowForSession()
+{
+ if (domainLineEdit->text().isEmpty())
+ return;
+ m_exceptionsModel->m_sessionCookies.append(domainLineEdit->text());
+ m_cookieJar->setAllowForSessionCookies(m_exceptionsModel->m_sessionCookies);
+ m_exceptionsModel->reset();
+}
diff --git a/src/cookiejar.h b/src/cookiejar.h
new file mode 100644
index 00000000..50d007f0
--- /dev/null
+++ b/src/cookiejar.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef COOKIEJAR_H
+#define COOKIEJAR_H
+
+#include <QtNetwork/QNetworkCookieJar>
+
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QStringList>
+
+#include <QtGui/QDialog>
+#include <QtGui/QTableView>
+
+QT_BEGIN_NAMESPACE
+class QSortFilterProxyModel;
+class QKeyEvent;
+QT_END_NAMESPACE
+
+class AutoSaver;
+
+class CookieJar : public QNetworkCookieJar
+{
+ friend class CookieModel;
+ Q_OBJECT
+ Q_PROPERTY(AcceptPolicy acceptPolicy READ acceptPolicy WRITE setAcceptPolicy)
+ Q_PROPERTY(KeepPolicy keepPolicy READ keepPolicy WRITE setKeepPolicy)
+ Q_PROPERTY(QStringList blockedCookies READ blockedCookies WRITE setBlockedCookies)
+ Q_PROPERTY(QStringList allowedCookies READ allowedCookies WRITE setAllowedCookies)
+ Q_PROPERTY(QStringList allowForSessionCookies READ allowForSessionCookies WRITE setAllowForSessionCookies)
+ Q_ENUMS(KeepPolicy)
+ Q_ENUMS(AcceptPolicy)
+
+signals:
+ void cookiesChanged();
+
+public:
+ enum AcceptPolicy {
+ AcceptAlways,
+ AcceptNever,
+ AcceptOnlyFromSitesNavigatedTo
+ };
+
+ enum KeepPolicy {
+ KeepUntilExpire,
+ KeepUntilExit,
+ KeepUntilTimeLimit
+ };
+
+ CookieJar(QObject *parent = 0);
+ ~CookieJar();
+
+ QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
+ bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
+
+ AcceptPolicy acceptPolicy() const;
+ void setAcceptPolicy(AcceptPolicy policy);
+
+ KeepPolicy keepPolicy() const;
+ void setKeepPolicy(KeepPolicy policy);
+
+ QStringList blockedCookies() const;
+ QStringList allowedCookies() const;
+ QStringList allowForSessionCookies() const;
+
+ void setBlockedCookies(const QStringList &list);
+ void setAllowedCookies(const QStringList &list);
+ void setAllowForSessionCookies(const QStringList &list);
+
+public slots:
+ void clear();
+ void loadSettings();
+
+private slots:
+ void save();
+
+private:
+ void purgeOldCookies();
+ void load();
+ bool m_loaded;
+ AutoSaver *m_saveTimer;
+
+ AcceptPolicy m_acceptCookies;
+ KeepPolicy m_keepCookies;
+
+ QStringList m_exceptions_block;
+ QStringList m_exceptions_allow;
+ QStringList m_exceptions_allowForSession;
+};
+
+class CookieModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ CookieModel(CookieJar *jar, QObject *parent = 0);
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+private slots:
+ void cookiesChanged();
+
+private:
+ CookieJar *m_cookieJar;
+};
+
+#include "ui_cookies.h"
+#include "ui_cookiesexceptions.h"
+
+class CookiesDialog : public QDialog, public Ui_CookiesDialog
+{
+ Q_OBJECT
+
+public:
+ CookiesDialog(CookieJar *cookieJar, QWidget *parent = 0);
+
+private:
+ QSortFilterProxyModel *m_proxyModel;
+};
+
+class CookieExceptionsModel : public QAbstractTableModel
+{
+ Q_OBJECT
+ friend class CookiesExceptionsDialog;
+
+public:
+ CookieExceptionsModel(CookieJar *cookieJar, QObject *parent = 0);
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+private:
+ CookieJar *m_cookieJar;
+
+ // Domains we allow, Domains we block, Domains we allow for this session
+ QStringList m_allowedCookies;
+ QStringList m_blockedCookies;
+ QStringList m_sessionCookies;
+};
+
+class CookiesExceptionsDialog : public QDialog, public Ui_CookiesExceptionsDialog
+{
+ Q_OBJECT
+
+public:
+ CookiesExceptionsDialog(CookieJar *cookieJar, QWidget *parent = 0);
+
+private slots:
+ void block();
+ void allow();
+ void allowForSession();
+ void textChanged(const QString &text);
+
+private:
+ CookieExceptionsModel *m_exceptionsModel;
+ QSortFilterProxyModel *m_proxyModel;
+ CookieJar *m_cookieJar;
+};
+
+#endif // COOKIEJAR_H
+
diff --git a/src/cookies.ui b/src/cookies.ui
new file mode 100644
index 00000000..c4bccc54
--- /dev/null
+++ b/src/cookies.ui
@@ -0,0 +1,106 @@
+<ui version="4.0" >
+ <class>CookiesDialog</class>
+ <widget class="QDialog" name="CookiesDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>550</width>
+ <height>370</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Cookies</string>
+ </property>
+ <layout class="QGridLayout" >
+ <item row="0" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>252</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1" >
+ <widget class="SearchLineEdit" name="search" />
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="EditTableView" name="cookiesTable" />
+ </item>
+ <item row="2" column="0" colspan="2" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QPushButton" name="removeButton" >
+ <property name="text" >
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeAllButton" >
+ <property name="text" >
+ <string>Remove &amp;All Cookies</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SearchLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>searchlineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>EditTableView</class>
+ <extends>QTableView</extends>
+ <header>edittableview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CookiesDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>472</x>
+ <y>329</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>461</x>
+ <y>356</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/cookiesexceptions.ui b/src/cookiesexceptions.ui
new file mode 100644
index 00000000..3d9ef624
--- /dev/null
+++ b/src/cookiesexceptions.ui
@@ -0,0 +1,184 @@
+<ui version="4.0" >
+ <class>CookiesExceptionsDialog</class>
+ <widget class="QDialog" name="CookiesExceptionsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>466</width>
+ <height>446</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Cookie Exceptions</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <item>
+ <widget class="QGroupBox" name="newExceptionGroupBox" >
+ <property name="title" >
+ <string>New Exception</string>
+ </property>
+ <layout class="QGridLayout" >
+ <item row="0" column="0" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Domain:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="domainLineEdit" />
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>81</width>
+ <height>25</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="blockButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Block</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="allowForSessionButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Allow For Session</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="allowButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Allow</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="ExceptionsGroupBox" >
+ <property name="title" >
+ <string>Exceptions</string>
+ </property>
+ <layout class="QGridLayout" >
+ <item row="0" column="0" colspan="3" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>252</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="3" >
+ <widget class="SearchLineEdit" name="search" />
+ </item>
+ <item row="1" column="0" colspan="4" >
+ <widget class="EditTableView" name="exceptionTable" />
+ </item>
+ <item row="2" column="0" >
+ <widget class="QPushButton" name="removeButton" >
+ <property name="text" >
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QPushButton" name="removeAllButton" >
+ <property name="text" >
+ <string>Remove &amp;All</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" colspan="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SearchLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>searchlineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>EditTableView</class>
+ <extends>QTableView</extends>
+ <header>edittableview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CookiesExceptionsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>381</x>
+ <y>428</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>336</x>
+ <y>443</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/data/addtab.png b/src/data/addtab.png
new file mode 100644
index 00000000..20928fb4
--- /dev/null
+++ b/src/data/addtab.png
Binary files differ
diff --git a/src/data/browser.svg b/src/data/browser.svg
new file mode 100644
index 00000000..c42af2bc
--- /dev/null
+++ b/src/data/browser.svg
@@ -0,0 +1,1598 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="277.35901"
+ height="277.43799"
+ viewBox="0 0 277.359 277.438"
+ id="Layer_1"
+ xml:space="preserve"><defs
+ id="defs285" />
+<pattern
+ x="64"
+ y="192.077"
+ width="69"
+ height="69"
+ patternUnits="userSpaceOnUse"
+ id="Polka_Dot_Pattern"
+ viewBox="2.125 -70.896 69 69"
+ overflow="visible">
+ <g
+ id="g4">
+ <polygon
+ fill="none"
+ points="71.125,-1.896 2.125,-1.896 2.125,-70.896 71.125,-70.896 "
+ id="polygon6" />
+ <polygon
+ fill="#F7BC60"
+ points="71.125,-1.896 2.125,-1.896 2.125,-70.896 71.125,-70.896 "
+ id="polygon8" />
+ <g
+ id="g10">
+ <path
+ fill="#FFFFFF"
+ d="M61.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path12" />
+ <path
+ fill="#FFFFFF"
+ d="M54.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path14" />
+ <path
+ fill="#FFFFFF"
+ d="M46.439-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path16" />
+ <path
+ fill="#FFFFFF"
+ d="M38.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path18" />
+ <path
+ fill="#FFFFFF"
+ d="M31.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path20" />
+ <path
+ fill="#FFFFFF"
+ d="M23.439-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path22" />
+ <path
+ fill="#FFFFFF"
+ d="M15.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path24" />
+ <path
+ fill="#FFFFFF"
+ d="M8.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path26" />
+ <path
+ fill="#FFFFFF"
+ d="M0.439-71.653c0.018,0.072,0.008,0.127-0.026,0.19C0.361-71.362,0.3-71.4,0.248-71.335 c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path28" />
+ </g>
+ <g
+ id="g30">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-71.653c0.018,0.072,0.008,0.127-0.026,0.19c-0.052,0.101-0.113,0.062-0.165,0.128 c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path32" />
+ </g>
+ <path
+ fill="#FFFFFF"
+ d="M0.495-71.653c0.018,0.072,0.008,0.127-0.026,0.19c-0.052,0.101-0.113,0.062-0.165,0.128 c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224C0.5-71.68,0.503-71.744,0.51-71.626 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path34" />
+ <g
+ id="g36">
+ <g
+ id="g38">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path40" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path42" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path44" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path46" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path48" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path50" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path52" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path54" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path56" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143C2-61.45,2.217-61.397,2.391-61.46c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path58" />
+ </g>
+ <g
+ id="g60">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path62" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path64" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path66" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path68" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path70" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path72" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path74" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path76" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path78" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-56.374,0.503-56.438,0.51-56.32 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path80" />
+ </g>
+ <g
+ id="g82">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path84" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path86" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path88" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path90" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path92" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path94" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path96" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path98" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path100" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path102" />
+ </g>
+ <g
+ id="g104">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path106" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path108" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path110" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path112" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path114" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path116" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path118" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path120" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 C8.15-41.004,8.149-41.02,8.14-41.04"
+ id="path122" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path124" />
+ </g>
+ <g
+ id="g126">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path128" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path130" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path132" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path134" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path136" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path138" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path140" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path142" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path144" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-33.416,0.503-33.48,0.51-33.362 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path146" />
+ </g>
+ <g
+ id="g148">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path150" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path152" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path154" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path156" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path158" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path160" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path162" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path164" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path166" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path168" />
+ </g>
+ <g
+ id="g170">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path172" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path174" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path176" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path178" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path180" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path182" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path184" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path186" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path188" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-18.11,0.503-18.175,0.51-18.057 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path190" />
+ </g>
+ <g
+ id="g192">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362C69-9.692,69.159-9.523,69.154-9.4c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path194" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path196" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path198" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path200" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path202" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path204" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path206" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053C17.933-7.969,17.839-8.227,18-8.34 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path208" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 C7.915-10.05,7.866-9.836,7.886-9.75C7.717-9.692,7.876-9.523,7.871-9.4C7.868-9.351,7.83-9.295,7.826-9.239 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C9.114-7.652,9.321-7.799,9.48-7.837c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path210" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 C0.254-10.05,0.205-9.836,0.225-9.75C0.056-9.692,0.215-9.523,0.21-9.4c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37C0.33-8.671,0.501-8.456,0.668-8.325c0.19,0.148,0.365,0.572,0.608,0.631 C1.454-7.652,1.66-7.799,1.819-7.837C2-7.88,2.217-7.827,2.391-7.89c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46C3.477-8.933,3.471-8.995,3.5-9.071 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path212" />
+ </g>
+ </g>
+ <g
+ id="g214">
+ <path
+ fill="#FFFFFF"
+ d="M69.439-2.778c0.018,0.072,0.008,0.127-0.026,0.19C69.361-2.487,69.3-2.525,69.248-2.46 c-0.051,0.062-0.099,0.276-0.079,0.362C69-2.04,69.159-1.871,69.154-1.748c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C70.397,0,70.604-0.146,70.763-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path216" />
+ <path
+ fill="#FFFFFF"
+ d="M61.778-2.778c0.018,0.072,0.007,0.127-0.026,0.19C61.7-2.487,61.64-2.525,61.587-2.46 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C62.737,0,62.943-0.146,63.103-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C61.915-3.117,61.78-3.02,61.781-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path218" />
+ <path
+ fill="#FFFFFF"
+ d="M54.118-2.778c0.018,0.072,0.007,0.127-0.026,0.19C54.04-2.487,53.98-2.525,53.927-2.46 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C55.077,0,55.283-0.146,55.442-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C54.255-3.117,54.12-3.02,54.121-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path220" />
+ <path
+ fill="#FFFFFF"
+ d="M46.458-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C47.416,0,47.623-0.146,47.782-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C46.594-3.117,46.459-3.02,46.46-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path222" />
+ <path
+ fill="#FFFFFF"
+ d="M38.797-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C39.756,0,39.962-0.146,40.122-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C38.934-3.117,38.799-3.02,38.8-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path224" />
+ <path
+ fill="#FFFFFF"
+ d="M31.137-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C32.095,0,32.302-0.146,32.461-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224C31.273-3.117,31.139-3.02,31.14-2.92c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path226" />
+ <path
+ fill="#FFFFFF"
+ d="M23.477-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C24.435,0,24.642-0.146,24.801-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 c-0.021,0.011-0.021-0.005-0.03-0.025"
+ id="path228" />
+ <path
+ fill="#FFFFFF"
+ d="M15.816-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C16.774,0,16.981-0.146,17.14-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207 c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169 C15.81-2.74,15.809-2.756,15.8-2.776"
+ id="path230" />
+ <path
+ fill="#FFFFFF"
+ d="M8.156-2.778c0.018,0.072,0.007,0.127-0.026,0.19C8.077-2.487,8.018-2.525,7.965-2.46 c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35C7.868-1.698,7.83-1.643,7.826-1.587 c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631 C9.114,0,9.321-0.146,9.48-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221 c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789C8.954-3.54,8.847-3.448,8.692-3.367 c-0.17,0.088-0.139,0.166-0.318,0.224C8.292-3.117,8.158-3.02,8.159-2.92C8.16-2.805,8.164-2.869,8.17-2.751 C8.15-2.74,8.149-2.756,8.14-2.776"
+ id="path232" />
+ <path
+ fill="#FFFFFF"
+ d="M0.495-2.778c0.018,0.072,0.008,0.127-0.026,0.19C0.417-2.487,0.356-2.525,0.304-2.46 C0.253-2.397,0.205-2.184,0.225-2.098C0.056-2.04,0.215-1.871,0.21-1.748c-0.002,0.05-0.041,0.105-0.045,0.161 c-0.01,0.119,0.017,0.266,0.068,0.37C0.33-1.019,0.501-0.804,0.668-0.673c0.19,0.148,0.365,0.572,0.608,0.631 C1.454,0,1.66-0.146,1.819-0.185C2-0.228,2.217-0.175,2.391-0.237c0.222-0.079,0.127-0.337,0.288-0.45 c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46C3.477-1.28,3.471-1.343,3.5-1.419 c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337 c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789C1.293-3.54,1.187-3.448,1.031-3.367 c-0.17,0.088-0.139,0.166-0.318,0.224C0.632-3.117,0.498-3.02,0.498-2.92C0.5-2.805,0.503-2.869,0.51-2.751 C0.489-2.74,0.488-2.756,0.479-2.776"
+ id="path234" />
+ </g>
+ </g>
+</pattern>
+<g
+ id="g236">
+ <path
+ d="M 44.233,0.368 C 42.032,0.004 39.876,-0.083 37.788,0.079 L 37.784,0.078 C 37.784,0.078 23.532,1.048 22.886,1.099 C 9.875,2.138 0,12.834 0,27.605 L 0,201.792 L 14.567,215.37 L 160.968,190.766 C 171.041,189.016 178.999,177.133 178.999,164.303 L 178.999,22.46 L 44.233,0.368 z"
+ id="path238"
+ style="fill:#024c1c" />
+ <path
+ d="M 179,164.304 C 179,177.134 171.042,189.017 160.969,190.767 L 14.567,215.37 L 14.567,26.683 C 14.567,9.52 28.263,-2.264 44.231,0.368 L 179,22.462 L 179,164.304 z"
+ id="path240"
+ style="fill:#66b036" />
+ <g
+ id="g242">
+ <path
+ d="M 133.897,47.137 L 145.72,48.411 L 145.72,69.158 L 159.025,70.099 L 159.025,83.113 L 145.72,82.502 L 145.72,130.066 C 145.72,134.207 146.176,136.869 147.093,138.064 C 147.919,139.158 149.195,139.697 150.907,139.697 C 151.069,139.697 151.24,139.695 151.414,139.683 C 154.031,139.533 156.878,138.728 159.98,137.314 L 159.98,149.275 C 154.707,151.591 149.532,152.966 144.452,153.398 C 143.716,153.457 143.005,153.486 142.317,153.486 C 137.716,153.486 134.199,152.152 131.797,149.451 C 128.998,146.318 127.598,141.285 127.598,134.387 L 127.598,81.661 L 121.209,81.368 L 121.209,67.424 L 129,67.985 L 133.897,47.137 z"
+ id="path244"
+ style="fill:#ffffff" />
+ </g>
+ <polygon
+ points="159.027,83.112 145.722,82.501 145.722,82.785 152.854,83.112 159.027,83.112 "
+ id="polygon246"
+ style="fill:#0a6333" />
+ <path
+ d="M 148.488,139.21 C 149.168,139.548 149.96,139.696 150.908,139.696 C 151.07,139.696 151.241,139.694 151.415,139.682 C 154.032,139.532 156.879,138.727 159.981,137.313 L 153.806,137.313 C 151.938,138.169 150.178,138.808 148.488,139.21 z"
+ id="path248"
+ style="fill:#024c1c" />
+ <path
+ d="M 133.897,47.137 L 127.723,47.137 L 122.93,67.549 L 129,67.985 L 133.897,47.137 z M 131.799,149.45 C 129,146.317 127.6,141.284 127.6,134.386 L 127.6,81.661 L 121.211,81.368 L 121.211,67.424 L 115.03,67.424 L 115.03,70.539 C 115.926,73.897 116.63,77.539 117.149,81.465 L 121.426,81.661 L 121.426,134.386 C 121.426,141.284 122.827,146.318 125.625,149.45 C 128.029,152.151 131.541,153.485 136.141,153.485 L 142.318,153.485 C 137.718,153.485 134.2,152.151 131.799,149.45 z"
+ id="path250"
+ style="fill:#024c1c" />
+ <path
+ d="M 102.954,170.419 C 103.782,170.419 104.669,170.362 105.615,170.259 L 100.649,170.259 C 101.341,170.364 102.138,170.419 102.954,170.419 z"
+ id="path252"
+ style="fill:#0a6333" />
+ <path
+ d="M 112.036,139.78 C 107.81,149.749 101.365,156.27 92.542,159.288 C 93.43,163.856 94.778,166.929 96.567,168.55 C 97.955,169.796 100.094,170.419 102.958,170.419 C 103.782,170.419 104.671,170.362 105.615,170.259 L 105.615,183.736 L 99.497,184.539 C 97.692,184.771 95.98,184.889 94.361,184.889 C 89.001,184.889 84.665,183.59 81.402,180.961 C 77.085,177.496 73.899,170.805 71.857,160.908 C 62.48,158.91 55.166,152.945 50.103,142.937 C 44.965,132.769 42.349,117.895 42.349,98.441 C 42.349,77.466 45.927,61.985 52.971,52.169 C 58.912,43.885 67.202,39.812 77.634,39.812 C 79.306,39.812 81.033,39.916 82.809,40.124 C 95.081,41.539 103.977,47.329 109.77,57.362 C 115.453,67.177 118.243,81.244 118.243,99.721 C 118.242,116.643 116.186,129.954 112.036,139.78 z M 93.582,135.933 C 95.996,129.724 97.189,117.54 97.189,99.37 C 97.189,83.054 96.007,71.837 93.608,65.682 C 91.21,59.496 87.622,56.153 82.808,55.731 C 82.441,55.7 82.075,55.681 81.724,55.681 C 77.264,55.681 73.84,58.283 71.447,63.508 C 68.863,69.201 67.555,81.003 67.555,98.866 C 67.555,116.129 68.826,128.379 71.388,135.569 C 73.804,142.419 77.423,145.813 82.174,145.813 C 82.384,145.813 82.593,145.805 82.809,145.79 C 87.566,145.489 91.148,142.202 93.582,135.933"
+ id="path254"
+ style="fill:#ffffff" />
+ <path
+ d="M 84.708,183.003 C 84.59,182.95 84.477,182.896 84.361,182.839 C 84.349,182.835 84.336,182.829 84.323,182.821 C 84.218,182.77 84.115,182.716 84.011,182.663 C 83.991,182.653 83.971,182.642 83.948,182.63 C 83.854,182.579 83.761,182.528 83.667,182.476 C 83.636,182.46 83.609,182.443 83.579,182.427 C 83.494,182.38 83.412,182.331 83.328,182.284 C 83.286,182.263 83.25,182.239 83.209,182.214 C 83.137,182.171 83.062,182.128 82.994,182.083 C 82.943,182.054 82.897,182.024 82.848,181.993 C 82.785,181.954 82.726,181.915 82.663,181.876 C 82.606,181.837 82.552,181.798 82.492,181.759 C 82.442,181.726 82.392,181.693 82.342,181.659 C 82.272,181.612 82.206,181.563 82.141,181.518 C 82.101,181.489 82.061,181.463 82.021,181.432 C 81.943,181.377 81.866,181.319 81.79,181.26 C 81.764,181.239 81.735,181.221 81.708,181.199 C 81.607,181.121 81.505,181.039 81.402,180.959 C 77.085,177.494 73.899,170.803 71.857,160.906 C 62.48,158.908 55.166,152.943 50.103,142.935 C 44.965,132.767 42.349,117.893 42.349,98.439 C 42.349,77.464 45.927,61.983 52.971,52.167 C 58.912,43.883 67.202,39.81 77.634,39.81 C 77.67,39.81 71.114,39.806 71.114,39.806 L 71.114,39.81 C 60.694,39.818 52.411,43.89 46.476,52.167 C 39.434,61.984 35.855,77.465 35.855,98.439 C 35.855,117.892 38.469,132.767 43.609,142.935 C 48.671,152.943 55.983,158.908 65.361,160.906 C 67.403,170.802 70.588,177.494 74.904,180.959 C 78.168,183.588 82.507,184.887 87.867,184.887 C 87.967,184.887 88.07,184.887 88.17,184.885 L 93.861,184.885 C 90.361,184.828 87.306,184.203 84.716,183.006 C 84.712,183.007 84.708,183.007 84.708,183.003 z M 87.113,65.681 C 89.511,71.837 90.69,83.054 90.69,99.369 C 90.69,117.539 89.502,129.723 87.083,135.932 C 85.142,140.942 82.439,144.047 79.013,145.248 C 79.999,145.621 81.058,145.81 82.173,145.81 C 82.383,145.81 82.592,145.802 82.808,145.787 C 87.567,145.488 91.149,142.201 93.582,135.932 C 95.996,129.723 97.189,117.539 97.189,99.369 C 97.189,83.053 96.007,71.836 93.608,65.681 C 91.21,59.495 87.622,56.152 82.808,55.73 C 82.441,55.699 82.075,55.68 81.724,55.68 C 80.601,55.68 79.549,55.845 78.556,56.173 L 78.556,56.175 L 78.556,56.175 C 82.254,57.322 85.104,60.5 87.113,65.681 z"
+ id="path256"
+ style="fill:#024c1c" />
+</g>
+
+<image
+ xlink:href="data:image/png;base64,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
+DwAAAABJRU5ErkJggg==
+"
+ x="98.21727"
+ y="99.928093"
+ width="177.23477"
+ height="175.07507"
+ id="image2749" /></svg> \ No newline at end of file
diff --git a/src/data/closetab.png b/src/data/closetab.png
new file mode 100644
index 00000000..ab9d669e
--- /dev/null
+++ b/src/data/closetab.png
Binary files differ
diff --git a/src/data/data.qrc b/src/data/data.qrc
new file mode 100644
index 00000000..c7d0294c
--- /dev/null
+++ b/src/data/data.qrc
@@ -0,0 +1,11 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>addtab.png</file>
+ <file>closetab.png</file>
+ <file>history.png</file>
+ <file>browser.svg</file>
+ <file>defaultbookmarks.xbel</file>
+ <file>loading.gif</file>
+ <file>defaulticon.png</file>
+</qresource>
+</RCC>
diff --git a/src/data/defaultbookmarks.xbel b/src/data/defaultbookmarks.xbel
new file mode 100644
index 00000000..f1c9253d
--- /dev/null
+++ b/src/data/defaultbookmarks.xbel
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE xbel>
+<xbel version="1.0">
+ <folder folded="yes">
+ <title>Bookmarks Bar</title>
+ <bookmark href="http://trolltech.com/">
+ <title>Trolltech.com</title>
+ </bookmark>
+ <bookmark href="http://webkit.org/">
+ <title>WebKit.org</title>
+ </bookmark>
+ <bookmark href="http://doc.trolltech.com/">
+ <title>Qt Documentation</title>
+ </bookmark>
+ <bookmark href="http://doc.trolltech.com/qq/">
+ <title>Qt Quarterly</title>
+ </bookmark>
+ <bookmark href="http://labs.trolltech.com/">
+ <title>Trolltech Labs</title>
+ </bookmark>
+ <bookmark href="http://www.qtcentre.org/">
+ <title>Qt Centre</title>
+ </bookmark>
+ <bookmark href="http://qt-apps.org/">
+ <title>Qt-Apps.org</title>
+ </bookmark>
+ <bookmark href="http://qtnode.net/">
+ <title>qtnode</title>
+ </bookmark>
+ <bookmark href="http://xkcd.com/">
+ <title>xkcd</title>
+ </bookmark>
+ </folder>
+ <folder folded="yes">
+ <title>Bookmarks Menu</title>
+ <bookmark href="http://reddit.com/">
+ <title>reddit.com: what's new online!</title>
+ </bookmark>
+ </folder>
+</xbel>
diff --git a/src/data/defaulticon.png b/src/data/defaulticon.png
new file mode 100644
index 00000000..01a0920c
--- /dev/null
+++ b/src/data/defaulticon.png
Binary files differ
diff --git a/src/data/history.png b/src/data/history.png
new file mode 100644
index 00000000..552a1cbd
--- /dev/null
+++ b/src/data/history.png
Binary files differ
diff --git a/src/data/loading.gif b/src/data/loading.gif
new file mode 100644
index 00000000..c1545eb0
--- /dev/null
+++ b/src/data/loading.gif
Binary files differ
diff --git a/src/downloaditem.ui b/src/downloaditem.ui
new file mode 100644
index 00000000..4a0a0fd9
--- /dev/null
+++ b/src/downloaditem.ui
@@ -0,0 +1,134 @@
+<ui version="4.0" >
+ <class>DownloadItem</class>
+ <widget class="QWidget" name="DownloadItem" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>423</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="fileIcon" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string>Ico</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2" >
+ <item>
+ <widget class="SqueezeLabel" native="1" name="fileNameLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" stdset="0" >
+ <string>Filename</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar" >
+ <property name="value" >
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="SqueezeLabel" native="1" name="downloadInfoLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Minimum" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" stdset="0" >
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>17</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="tryAgainButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Try Again</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="stopButton" >
+ <property name="text" >
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="openButton" >
+ <property name="text" >
+ <string>Open</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>17</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SqueezeLabel</class>
+ <extends>QWidget</extends>
+ <header>squeezelabel.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
new file mode 100644
index 00000000..d8f19c5e
--- /dev/null
+++ b/src/downloadmanager.cpp
@@ -0,0 +1,575 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "downloadmanager.h"
+
+#include "autosaver.h"
+#include "browserapplication.h"
+#include "networkaccessmanager.h"
+
+#include <math.h>
+
+#include <QtCore/QMetaEnum>
+#include <QtCore/QSettings>
+
+#include <QtGui/QDesktopServices>
+#include <QtGui/QFileDialog>
+#include <QtGui/QHeaderView>
+#include <QtGui/QFileIconProvider>
+
+#include <QtCore/QDebug>
+
+#include <QtWebKit/QWebSettings>
+
+/*!
+ DownloadItem is a widget that is displayed in the download manager list.
+ It moves the data from the QNetworkReply into the QFile as well
+ as update the information/progressbar and report errors.
+ */
+DownloadItem::DownloadItem(QNetworkReply *reply, bool requestFileName, QWidget *parent)
+ : QWidget(parent)
+ , m_reply(reply)
+ , m_requestFileName(requestFileName)
+ , m_bytesReceived(0)
+{
+ setupUi(this);
+ QPalette p = downloadInfoLabel->palette();
+ p.setColor(QPalette::Text, Qt::darkGray);
+ downloadInfoLabel->setPalette(p);
+ progressBar->setMaximum(0);
+ tryAgainButton->hide();
+ connect(stopButton, SIGNAL(clicked()), this, SLOT(stop()));
+ connect(openButton, SIGNAL(clicked()), this, SLOT(open()));
+ connect(tryAgainButton, SIGNAL(clicked()), this, SLOT(tryAgain()));
+
+ init();
+}
+
+void DownloadItem::init()
+{
+ if (!m_reply)
+ return;
+
+ // attach to the m_reply
+ m_url = m_reply->url();
+ m_reply->setParent(this);
+ connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
+ connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
+ this, SLOT(error(QNetworkReply::NetworkError)));
+ connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
+ this, SLOT(downloadProgress(qint64, qint64)));
+ connect(m_reply, SIGNAL(metaDataChanged()),
+ this, SLOT(metaDataChanged()));
+ connect(m_reply, SIGNAL(finished()),
+ this, SLOT(finished()));
+
+ // reset info
+ downloadInfoLabel->clear();
+ progressBar->setValue(0);
+ getFileName();
+
+ // start timer for the download estimation
+ m_downloadTime.start();
+
+ if (m_reply->error() != QNetworkReply::NoError) {
+ error(m_reply->error());
+ finished();
+ }
+}
+
+void DownloadItem::getFileName()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ QString defaultLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
+ QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString();
+ if (!downloadDirectory.isEmpty())
+ downloadDirectory += QLatin1Char('/');
+
+ QString defaultFileName = saveFileName(downloadDirectory);
+ QString fileName = defaultFileName;
+ if (m_requestFileName) {
+ fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
+ if (fileName.isEmpty()) {
+ m_reply->close();
+ fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName()));
+ return;
+ }
+ }
+ m_output.setFileName(fileName);
+ fileNameLabel->setText(QFileInfo(m_output.fileName()).fileName());
+ if (m_requestFileName)
+ downloadReadyRead();
+}
+
+QString DownloadItem::saveFileName(const QString &directory) const
+{
+ // Move this function into QNetworkReply to also get file name sent from the server
+ QString path = m_url.path();
+ QFileInfo info(path);
+ QString baseName = info.completeBaseName();
+ QString endName = info.suffix();
+
+ if (baseName.isEmpty()) {
+ baseName = QLatin1String("unnamed_download");
+ qDebug() << "DownloadManager:: downloading unknown file:" << m_url;
+ }
+ QString name = directory + baseName + QLatin1Char('.') + endName;
+ if (QFile::exists(name)) {
+ // already exists, don't overwrite
+ int i = 1;
+ do {
+ name = directory + baseName + QLatin1Char('-') + QString::number(i++) + QLatin1Char('.') + endName;
+ } while (QFile::exists(name));
+ }
+ return name;
+}
+
+
+void DownloadItem::stop()
+{
+ setUpdatesEnabled(false);
+ stopButton->setEnabled(false);
+ stopButton->hide();
+ tryAgainButton->setEnabled(true);
+ tryAgainButton->show();
+ setUpdatesEnabled(true);
+ m_reply->abort();
+}
+
+void DownloadItem::open()
+{
+ QFileInfo info(m_output);
+ QUrl url = QUrl::fromLocalFile(info.absolutePath());
+ QDesktopServices::openUrl(url);
+}
+
+void DownloadItem::tryAgain()
+{
+ if (!tryAgainButton->isEnabled())
+ return;
+
+ tryAgainButton->setEnabled(false);
+ tryAgainButton->setVisible(false);
+ stopButton->setEnabled(true);
+ stopButton->setVisible(true);
+ progressBar->setVisible(true);
+
+ QNetworkReply *r = BrowserApplication::networkAccessManager()->get(QNetworkRequest(m_url));
+ if (m_reply)
+ m_reply->deleteLater();
+ if (m_output.exists())
+ m_output.remove();
+ m_reply = r;
+ init();
+ emit statusChanged();
+}
+
+void DownloadItem::downloadReadyRead()
+{
+ if (m_requestFileName && m_output.fileName().isEmpty())
+ return;
+ if (!m_output.isOpen()) {
+ // in case someone else has already put a file there
+ if (!m_requestFileName)
+ getFileName();
+ if (!m_output.open(QIODevice::WriteOnly)) {
+ downloadInfoLabel->setText(tr("Error opening save file: %1")
+ .arg(m_output.errorString()));
+ stopButton->click();
+ emit statusChanged();
+ return;
+ }
+ emit statusChanged();
+ }
+ if (-1 == m_output.write(m_reply->readAll())) {
+ downloadInfoLabel->setText(tr("Error saving: %1")
+ .arg(m_output.errorString()));
+ stopButton->click();
+ }
+}
+
+void DownloadItem::error(QNetworkReply::NetworkError)
+{
+ qDebug() << "DownloadItem::error" << m_reply->errorString() << m_url;
+ downloadInfoLabel->setText(tr("Network Error: %1").arg(m_reply->errorString()));
+ tryAgainButton->setEnabled(true);
+ tryAgainButton->setVisible(true);
+}
+
+void DownloadItem::metaDataChanged()
+{
+ qDebug() << "DownloadItem::metaDataChanged: not handled.";
+}
+
+void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+{
+ m_bytesReceived = bytesReceived;
+ if (bytesTotal == -1) {
+ progressBar->setValue(0);
+ progressBar->setMaximum(0);
+ } else {
+ progressBar->setValue(bytesReceived);
+ progressBar->setMaximum(bytesTotal);
+ }
+ updateInfoLabel();
+}
+
+void DownloadItem::updateInfoLabel()
+{
+ if (m_reply->error() == QNetworkReply::NoError)
+ return;
+
+ qint64 bytesTotal = progressBar->maximum();
+ bool running = !downloadedSuccessfully();
+
+ // update info label
+ double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
+ double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed;
+ QString timeRemainingString = tr("seconds");
+ if (timeRemaining > 60) {
+ timeRemaining = timeRemaining / 60;
+ timeRemainingString = tr("minutes");
+ }
+ timeRemaining = floor(timeRemaining);
+
+ // When downloading the eta should never be 0
+ if (timeRemaining == 0)
+ timeRemaining = 1;
+
+ QString info;
+ if (running) {
+ QString remaining;
+ if (bytesTotal != 0)
+ remaining = tr("- %4 %5 remaining")
+ .arg(timeRemaining)
+ .arg(timeRemainingString);
+ info = QString(tr("%1 of %2 (%3/sec) %4"))
+ .arg(dataString(m_bytesReceived))
+ .arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal))
+ .arg(dataString((int)speed))
+ .arg(remaining);
+ } else {
+ if (m_bytesReceived == bytesTotal)
+ info = dataString(m_output.size());
+ else
+ info = tr("%1 of %2 - Stopped")
+ .arg(dataString(m_bytesReceived))
+ .arg(dataString(bytesTotal));
+ }
+ downloadInfoLabel->setText(info);
+}
+
+QString DownloadItem::dataString(int size) const
+{
+ QString unit;
+ if (size < 1024) {
+ unit = tr("bytes");
+ } else if (size < 1024*1024) {
+ size /= 1024;
+ unit = tr("kB");
+ } else {
+ size /= 1024*1024;
+ unit = tr("MB");
+ }
+ return QString(QLatin1String("%1 %2")).arg(size).arg(unit);
+}
+
+bool DownloadItem::downloading() const
+{
+ return (progressBar->isVisible());
+}
+
+bool DownloadItem::downloadedSuccessfully() const
+{
+ return (stopButton->isHidden() && tryAgainButton->isHidden());
+}
+
+void DownloadItem::finished()
+{
+ progressBar->hide();
+ stopButton->setEnabled(false);
+ stopButton->hide();
+ m_output.close();
+ updateInfoLabel();
+ emit statusChanged();
+}
+
+/*!
+ DownloadManager is a Dialog that contains a list of DownloadItems
+
+ It is a basic download manager. It only downloads the file, doesn't do BitTorrent,
+ extract zipped files or anything fancy.
+ */
+DownloadManager::DownloadManager(QWidget *parent)
+ : QDialog(parent)
+ , m_autoSaver(new AutoSaver(this))
+ , m_manager(BrowserApplication::networkAccessManager())
+ , m_iconProvider(0)
+ , m_removePolicy(Never)
+{
+ setupUi(this);
+ downloadsView->setShowGrid(false);
+ downloadsView->verticalHeader()->hide();
+ downloadsView->horizontalHeader()->hide();
+ downloadsView->setAlternatingRowColors(true);
+ downloadsView->horizontalHeader()->setStretchLastSection(true);
+ m_model = new DownloadModel(this);
+ downloadsView->setModel(m_model);
+ connect(cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup()));
+ load();
+}
+
+DownloadManager::~DownloadManager()
+{
+ m_autoSaver->changeOccurred();
+ m_autoSaver->saveIfNeccessary();
+ if (m_iconProvider)
+ delete m_iconProvider;
+}
+
+int DownloadManager::activeDownloads() const
+{
+ int count = 0;
+ for (int i = 0; i < m_downloads.count(); ++i) {
+ if (m_downloads.at(i)->stopButton->isEnabled())
+ ++count;
+ }
+ return count;
+}
+
+void DownloadManager::download(const QNetworkRequest &request, bool requestFileName)
+{
+ if (request.url().isEmpty())
+ return;
+ handleUnsupportedContent(m_manager->get(request), requestFileName);
+}
+
+void DownloadManager::handleUnsupportedContent(QNetworkReply *reply, bool requestFileName)
+{
+ if (!reply || reply->url().isEmpty())
+ return;
+ QVariant header = reply->header(QNetworkRequest::ContentLengthHeader);
+ bool ok;
+ int size = header.toInt(&ok);
+ if (ok && size == 0)
+ return;
+
+ qDebug() << "DownloadManager::handleUnsupportedContent" << reply->url() << "requestFileName" << requestFileName;
+ DownloadItem *item = new DownloadItem(reply, requestFileName, this);
+ addItem(item);
+}
+
+void DownloadManager::addItem(DownloadItem *item)
+{
+ connect(item, SIGNAL(statusChanged()), this, SLOT(updateRow()));
+ int row = m_downloads.count();
+ m_model->beginInsertRows(QModelIndex(), row, row);
+ m_downloads.append(item);
+ m_model->endInsertRows();
+ updateItemCount();
+ if (row == 0)
+ show();
+ downloadsView->setIndexWidget(m_model->index(row, 0), item);
+ QIcon icon = style()->standardIcon(QStyle::SP_FileIcon);
+ item->fileIcon->setPixmap(icon.pixmap(48, 48));
+ downloadsView->setRowHeight(row, item->sizeHint().height());
+}
+
+void DownloadManager::updateRow()
+{
+ DownloadItem *item = qobject_cast<DownloadItem*>(sender());
+ int row = m_downloads.indexOf(item);
+ if (-1 == row)
+ return;
+ if (!m_iconProvider)
+ m_iconProvider = new QFileIconProvider();
+ QIcon icon = m_iconProvider->icon(item->m_output.fileName());
+ if (icon.isNull())
+ icon = style()->standardIcon(QStyle::SP_FileIcon);
+ item->fileIcon->setPixmap(icon.pixmap(48, 48));
+ downloadsView->setRowHeight(row, item->minimumSizeHint().height());
+
+ bool remove = false;
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (!item->downloading()
+ && globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
+ remove = true;
+
+ if (item->downloadedSuccessfully()
+ && removePolicy() == DownloadManager::SuccessFullDownload) {
+ remove = true;
+ }
+ if (remove)
+ m_model->removeRow(row);
+
+ cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
+}
+
+DownloadManager::RemovePolicy DownloadManager::removePolicy() const
+{
+ return m_removePolicy;
+}
+
+void DownloadManager::setRemovePolicy(RemovePolicy policy)
+{
+ if (policy == m_removePolicy)
+ return;
+ m_removePolicy = policy;
+ m_autoSaver->changeOccurred();
+}
+
+void DownloadManager::save() const
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
+ settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy)));
+ settings.setValue(QLatin1String("size"), size());
+ if (m_removePolicy == Exit)
+ return;
+
+ for (int i = 0; i < m_downloads.count(); ++i) {
+ QString key = QString(QLatin1String("download_%1_")).arg(i);
+ settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url);
+ settings.setValue(key + QLatin1String("location"), QFileInfo(m_downloads[i]->m_output).filePath());
+ settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully());
+ }
+ int i = m_downloads.count();
+ QString key = QString(QLatin1String("download_%1_")).arg(i);
+ while (settings.contains(key + QLatin1String("url"))) {
+ settings.remove(key + QLatin1String("url"));
+ settings.remove(key + QLatin1String("location"));
+ settings.remove(key + QLatin1String("done"));
+ key = QString(QLatin1String("download_%1_")).arg(++i);
+ }
+}
+
+void DownloadManager::load()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ QSize size = settings.value(QLatin1String("size")).toSize();
+ if (size.isValid())
+ resize(size);
+ QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray();
+ QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy"));
+ m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ?
+ Never :
+ static_cast<RemovePolicy>(removePolicyEnum.keyToValue(value));
+
+ int i = 0;
+ QString key = QString(QLatin1String("download_%1_")).arg(i);
+ while (settings.contains(key + QLatin1String("url"))) {
+ QUrl url = settings.value(key + QLatin1String("url")).toUrl();
+ QString fileName = settings.value(key + QLatin1String("location")).toString();
+ bool done = settings.value(key + QLatin1String("done"), true).toBool();
+ if (!url.isEmpty() && !fileName.isEmpty()) {
+ DownloadItem *item = new DownloadItem(0, this);
+ item->m_output.setFileName(fileName);
+ item->fileNameLabel->setText(QFileInfo(item->m_output.fileName()).fileName());
+ item->m_url = url;
+ item->stopButton->setVisible(false);
+ item->stopButton->setEnabled(false);
+ item->tryAgainButton->setVisible(!done);
+ item->tryAgainButton->setEnabled(!done);
+ item->progressBar->setVisible(!done);
+ addItem(item);
+ }
+ key = QString(QLatin1String("download_%1_")).arg(++i);
+ }
+ cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0);
+}
+
+void DownloadManager::cleanup()
+{
+ if (m_downloads.isEmpty())
+ return;
+ m_model->removeRows(0, m_downloads.count());
+ updateItemCount();
+ if (m_downloads.isEmpty() && m_iconProvider) {
+ delete m_iconProvider;
+ m_iconProvider = 0;
+ }
+ m_autoSaver->changeOccurred();
+}
+
+void DownloadManager::updateItemCount()
+{
+ int count = m_downloads.count();
+ itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count));
+}
+
+DownloadModel::DownloadModel(DownloadManager *downloadManager, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_downloadManager(downloadManager)
+{
+}
+
+QVariant DownloadModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() >= rowCount(index.parent()))
+ return QVariant();
+ if (role == Qt::ToolTipRole)
+ if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully())
+ return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text();
+ return QVariant();
+}
+
+int DownloadModel::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count();
+}
+
+bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (parent.isValid())
+ return false;
+
+ int lastRow = row + count - 1;
+ for (int i = lastRow; i >= row; --i) {
+ if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully()
+ || m_downloadManager->m_downloads.at(i)->tryAgainButton->isEnabled()) {
+ beginRemoveRows(parent, i, i);
+ m_downloadManager->m_downloads.takeAt(i)->deleteLater();
+ endRemoveRows();
+ }
+ }
+ m_downloadManager->m_autoSaver->changeOccurred();
+ return true;
+}
+
diff --git a/src/downloadmanager.h b/src/downloadmanager.h
new file mode 100644
index 00000000..6f27622b
--- /dev/null
+++ b/src/downloadmanager.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef DOWNLOADMANAGER_H
+#define DOWNLOADMANAGER_H
+
+#include "ui_downloads.h"
+#include "ui_downloaditem.h"
+
+#include <QtNetwork/QNetworkReply>
+
+#include <QtCore/QFile>
+#include <QtCore/QTime>
+
+class DownloadItem : public QWidget, public Ui_DownloadItem
+{
+ Q_OBJECT
+
+signals:
+ void statusChanged();
+
+public:
+ DownloadItem(QNetworkReply *reply = 0, bool requestFileName = false, QWidget *parent = 0);
+ bool downloading() const;
+ bool downloadedSuccessfully() const;
+
+ QUrl m_url;
+
+ QFile m_output;
+ QNetworkReply *m_reply;
+
+private slots:
+ void stop();
+ void tryAgain();
+ void open();
+
+ void downloadReadyRead();
+ void error(QNetworkReply::NetworkError code);
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ void metaDataChanged();
+ void finished();
+
+private:
+ void getFileName();
+ void init();
+ void updateInfoLabel();
+ QString dataString(int size) const;
+
+ QString saveFileName(const QString &directory) const;
+
+ bool m_requestFileName;
+ qint64 m_bytesReceived;
+ QTime m_downloadTime;
+};
+
+class AutoSaver;
+class DownloadModel;
+QT_BEGIN_NAMESPACE
+class QFileIconProvider;
+QT_END_NAMESPACE
+
+class DownloadManager : public QDialog, public Ui_DownloadDialog
+{
+ Q_OBJECT
+ Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy)
+ Q_ENUMS(RemovePolicy)
+
+public:
+ enum RemovePolicy {
+ Never,
+ Exit,
+ SuccessFullDownload
+ };
+
+ DownloadManager(QWidget *parent = 0);
+ ~DownloadManager();
+ int activeDownloads() const;
+
+ RemovePolicy removePolicy() const;
+ void setRemovePolicy(RemovePolicy policy);
+
+public slots:
+ void download(const QNetworkRequest &request, bool requestFileName = false);
+ inline void download(const QUrl &url, bool requestFileName = false)
+ { download(QNetworkRequest(url), requestFileName); }
+ void handleUnsupportedContent(QNetworkReply *reply, bool requestFileName = false);
+ void cleanup();
+
+private slots:
+ void save() const;
+ void updateRow();
+
+private:
+ void addItem(DownloadItem *item);
+ void updateItemCount();
+ void load();
+
+ AutoSaver *m_autoSaver;
+ DownloadModel *m_model;
+ QNetworkAccessManager *m_manager;
+ QFileIconProvider *m_iconProvider;
+ QList<DownloadItem*> m_downloads;
+ RemovePolicy m_removePolicy;
+ friend class DownloadModel;
+};
+
+class DownloadModel : public QAbstractListModel
+{
+ friend class DownloadManager;
+ Q_OBJECT
+
+public:
+ DownloadModel(DownloadManager *downloadManager, QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+private:
+ DownloadManager *m_downloadManager;
+
+};
+
+#endif // DOWNLOADMANAGER_H
+
diff --git a/src/downloads.ui b/src/downloads.ui
new file mode 100644
index 00000000..a2e25693
--- /dev/null
+++ b/src/downloads.ui
@@ -0,0 +1,83 @@
+<ui version="4.0" >
+ <class>DownloadDialog</class>
+ <widget class="QDialog" name="DownloadDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>332</width>
+ <height>252</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Downloads</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>0</number>
+ </property>
+ <item row="0" column="0" colspan="3" >
+ <widget class="EditTableView" name="downloadsView" />
+ </item>
+ <item row="1" column="0" >
+ <layout class="QHBoxLayout" name="horizontalLayout" >
+ <item>
+ <widget class="QPushButton" name="cleanupButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Clean up</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>58</width>
+ <height>24</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="itemCount" >
+ <property name="text" >
+ <string>0 Items</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <spacer name="horizontalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>148</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>EditTableView</class>
+ <extends>QTableView</extends>
+ <header>edittableview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/edittableview.cpp b/src/edittableview.cpp
new file mode 100644
index 00000000..d47ac78b
--- /dev/null
+++ b/src/edittableview.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "edittableview.h"
+#include <QtGui/QKeyEvent>
+
+EditTableView::EditTableView(QWidget *parent)
+ : QTableView(parent)
+{
+}
+
+void EditTableView::keyPressEvent(QKeyEvent *event)
+{
+ if ((event->key() == Qt::Key_Delete
+ || event->key() == Qt::Key_Backspace)
+ && model()) {
+ removeOne();
+ } else {
+ QAbstractItemView::keyPressEvent(event);
+ }
+}
+
+void EditTableView::removeOne()
+{
+ if (!model() || !selectionModel())
+ return;
+ int row = currentIndex().row();
+ model()->removeRow(row, rootIndex());
+ QModelIndex idx = model()->index(row, 0, rootIndex());
+ if (!idx.isValid())
+ idx = model()->index(row - 1, 0, rootIndex());
+ selectionModel()->select(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
+}
+
+void EditTableView::removeAll()
+{
+ if (model())
+ model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex());
+}
diff --git a/src/edittableview.h b/src/edittableview.h
new file mode 100644
index 00000000..f22c3ce9
--- /dev/null
+++ b/src/edittableview.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef EDITTABLEVIEW_H
+#define EDITTABLEVIEW_H
+
+#include <QtGui/QTableView>
+
+class EditTableView : public QTableView
+{
+ Q_OBJECT
+
+public:
+ EditTableView(QWidget *parent = 0);
+ void keyPressEvent(QKeyEvent *event);
+
+public slots:
+ void removeOne();
+ void removeAll();
+};
+
+#endif // EDITTABLEVIEW_H
+
diff --git a/src/edittreeview.cpp b/src/edittreeview.cpp
new file mode 100644
index 00000000..5e55fd16
--- /dev/null
+++ b/src/edittreeview.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "edittreeview.h"
+
+#include <QtGui/QKeyEvent>
+
+EditTreeView::EditTreeView(QWidget *parent)
+ : QTreeView(parent)
+{
+}
+
+void EditTreeView::keyPressEvent(QKeyEvent *event)
+{
+ if ((event->key() == Qt::Key_Delete
+ || event->key() == Qt::Key_Backspace)
+ && model()) {
+ removeOne();
+ } else {
+ QAbstractItemView::keyPressEvent(event);
+ }
+}
+
+void EditTreeView::removeOne()
+{
+ if (!model())
+ return;
+ QModelIndex ci = currentIndex();
+ int row = ci.row();
+ model()->removeRow(row, ci.parent());
+}
+
+void EditTreeView::removeAll()
+{
+ if (!model())
+ return;
+ model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex());
+}
+
diff --git a/src/edittreeview.h b/src/edittreeview.h
new file mode 100644
index 00000000..1341ad62
--- /dev/null
+++ b/src/edittreeview.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef EDITTREEVIEW_H
+#define EDITTREEVIEW_H
+
+#include <QtGui/QTreeView>
+
+class EditTreeView : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ EditTreeView(QWidget *parent = 0);
+ void keyPressEvent(QKeyEvent *event);
+
+public slots:
+ void removeOne();
+ void removeAll();
+};
+
+#endif // EDITTREEVIEW_H
+
diff --git a/src/history.cpp b/src/history.cpp
new file mode 100644
index 00000000..fef1920c
--- /dev/null
+++ b/src/history.cpp
@@ -0,0 +1,1277 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "history.h"
+
+#include "autosaver.h"
+#include "browserapplication.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QTextStream>
+
+#include <QtCore/QtAlgorithms>
+
+#include <QtGui/QClipboard>
+#include <QtGui/QDesktopServices>
+#include <QtGui/QHeaderView>
+#include <QtGui/QStyle>
+
+#include <QtWebKit/QWebHistoryInterface>
+#include <QtWebKit/QWebSettings>
+
+#include <QtCore/QDebug>
+
+static const unsigned int HISTORY_VERSION = 23;
+
+HistoryManager::HistoryManager(QObject *parent)
+ : QWebHistoryInterface(parent)
+ , m_saveTimer(new AutoSaver(this))
+ , m_historyLimit(30)
+ , m_historyModel(0)
+ , m_historyFilterModel(0)
+ , m_historyTreeModel(0)
+{
+ m_expiredTimer.setSingleShot(true);
+ connect(&m_expiredTimer, SIGNAL(timeout()),
+ this, SLOT(checkForExpired()));
+ connect(this, SIGNAL(entryAdded(const HistoryItem &)),
+ m_saveTimer, SLOT(changeOccurred()));
+ connect(this, SIGNAL(entryRemoved(const HistoryItem &)),
+ m_saveTimer, SLOT(changeOccurred()));
+ load();
+
+ m_historyModel = new HistoryModel(this, this);
+ m_historyFilterModel = new HistoryFilterModel(m_historyModel, this);
+ m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this);
+
+ // QWebHistoryInterface will delete the history manager
+ QWebHistoryInterface::setDefaultInterface(this);
+}
+
+HistoryManager::~HistoryManager()
+{
+ m_saveTimer->saveIfNeccessary();
+}
+
+QList<HistoryItem> HistoryManager::history() const
+{
+ return m_history;
+}
+
+bool HistoryManager::historyContains(const QString &url) const
+{
+ return m_historyFilterModel->historyContains(url);
+}
+
+void HistoryManager::addHistoryEntry(const QString &url)
+{
+ QUrl cleanUrl(url);
+ cleanUrl.setPassword(QString());
+ cleanUrl.setHost(cleanUrl.host().toLower());
+ HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime());
+ addHistoryItem(item);
+}
+
+void HistoryManager::setHistory(const QList<HistoryItem> &history, bool loadedAndSorted)
+{
+ m_history = history;
+
+ // verify that it is sorted by date
+ if (!loadedAndSorted)
+ qSort(m_history.begin(), m_history.end());
+
+ checkForExpired();
+
+ if (loadedAndSorted) {
+ m_lastSavedUrl = m_history.value(0).url;
+ } else {
+ m_lastSavedUrl = QString();
+ m_saveTimer->changeOccurred();
+ }
+ emit historyReset();
+}
+
+HistoryModel *HistoryManager::historyModel() const
+{
+ return m_historyModel;
+}
+
+HistoryFilterModel *HistoryManager::historyFilterModel() const
+{
+ return m_historyFilterModel;
+}
+
+HistoryTreeModel *HistoryManager::historyTreeModel() const
+{
+ return m_historyTreeModel;
+}
+
+void HistoryManager::checkForExpired()
+{
+ if (m_historyLimit < 0 || m_history.isEmpty())
+ return;
+
+ QDateTime now = QDateTime::currentDateTime();
+ int nextTimeout = 0;
+
+ while (!m_history.isEmpty()) {
+ QDateTime checkForExpired = m_history.last().dateTime;
+ checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit));
+ if (now.daysTo(checkForExpired) > 7) {
+ // check at most in a week to prevent int overflows on the timer
+ nextTimeout = 7 * 86400;
+ } else {
+ nextTimeout = now.secsTo(checkForExpired);
+ }
+ if (nextTimeout > 0)
+ break;
+ HistoryItem item = m_history.takeLast();
+ // remove from saved file also
+ m_lastSavedUrl = QString();
+ emit entryRemoved(item);
+ }
+
+ if (nextTimeout > 0)
+ m_expiredTimer.start(nextTimeout * 1000);
+}
+
+void HistoryManager::addHistoryItem(const HistoryItem &item)
+{
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled))
+ return;
+
+ m_history.prepend(item);
+ emit entryAdded(item);
+ if (m_history.count() == 1)
+ checkForExpired();
+}
+
+void HistoryManager::updateHistoryItem(const QUrl &url, const QString &title)
+{
+ for (int i = 0; i < m_history.count(); ++i) {
+ if (url == m_history.at(i).url) {
+ m_history[i].title = title;
+ m_saveTimer->changeOccurred();
+ if (m_lastSavedUrl.isEmpty())
+ m_lastSavedUrl = m_history.at(i).url;
+ emit entryUpdated(i);
+ break;
+ }
+ }
+}
+
+int HistoryManager::historyLimit() const
+{
+ return m_historyLimit;
+}
+
+void HistoryManager::setHistoryLimit(int limit)
+{
+ if (m_historyLimit == limit)
+ return;
+ m_historyLimit = limit;
+ checkForExpired();
+ m_saveTimer->changeOccurred();
+}
+
+void HistoryManager::clear()
+{
+ m_history.clear();
+ m_lastSavedUrl = QString();
+ m_saveTimer->changeOccurred();
+ m_saveTimer->saveIfNeccessary();
+ historyReset();
+}
+
+void HistoryManager::loadSettings()
+{
+ // load settings
+ QSettings settings;
+ settings.beginGroup(QLatin1String("history"));
+ m_historyLimit = settings.value(QLatin1String("historyLimit"), 30).toInt();
+}
+
+void HistoryManager::load()
+{
+ loadSettings();
+
+ QFile historyFile(QDesktopServices::storageLocation(QDesktopServices::DataLocation)
+ + QLatin1String("/history"));
+ if (!historyFile.exists())
+ return;
+ if (!historyFile.open(QFile::ReadOnly)) {
+ qWarning() << "Unable to open history file" << historyFile.fileName();
+ return;
+ }
+
+ QList<HistoryItem> list;
+ QDataStream in(&historyFile);
+ // Double check that the history file is sorted as it is read in
+ bool needToSort = false;
+ HistoryItem lastInsertedItem;
+ QByteArray data;
+ QDataStream stream;
+ QBuffer buffer;
+ stream.setDevice(&buffer);
+ while (!historyFile.atEnd()) {
+ in >> data;
+ buffer.close();
+ buffer.setBuffer(&data);
+ buffer.open(QIODevice::ReadOnly);
+ quint32 ver;
+ stream >> ver;
+ if (ver != HISTORY_VERSION)
+ continue;
+ HistoryItem item;
+ stream >> item.url;
+ stream >> item.dateTime;
+ stream >> item.title;
+
+ if (!item.dateTime.isValid())
+ continue;
+
+ if (item == lastInsertedItem) {
+ if (lastInsertedItem.title.isEmpty() && !list.isEmpty())
+ list[0].title = item.title;
+ continue;
+ }
+
+ if (!needToSort && !list.isEmpty() && lastInsertedItem < item)
+ needToSort = true;
+
+ list.prepend(item);
+ lastInsertedItem = item;
+ }
+ if (needToSort)
+ qSort(list.begin(), list.end());
+
+ setHistory(list, true);
+
+ // If we had to sort re-write the whole history sorted
+ if (needToSort) {
+ m_lastSavedUrl = QString();
+ m_saveTimer->changeOccurred();
+ }
+}
+
+void HistoryManager::save()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("history"));
+ settings.setValue(QLatin1String("historyLimit"), m_historyLimit);
+
+ bool saveAll = m_lastSavedUrl.isEmpty();
+ int first = m_history.count() - 1;
+ if (!saveAll) {
+ // find the first one to save
+ for (int i = 0; i < m_history.count(); ++i) {
+ if (m_history.at(i).url == m_lastSavedUrl) {
+ first = i - 1;
+ break;
+ }
+ }
+ }
+ if (first == m_history.count() - 1)
+ saveAll = true;
+
+ QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ if (directory.isEmpty())
+ directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName();
+ if (!QFile::exists(directory)) {
+ QDir dir;
+ dir.mkpath(directory);
+ }
+
+ QFile historyFile(directory + QLatin1String("/history"));
+ // When saving everything use a temporary file to prevent possible data loss.
+ QTemporaryFile tempFile;
+ tempFile.setAutoRemove(false);
+ bool open = false;
+ if (saveAll) {
+ open = tempFile.open();
+ } else {
+ open = historyFile.open(QFile::Append);
+ }
+
+ if (!open) {
+ qWarning() << "Unable to open history file for saving"
+ << (saveAll ? tempFile.fileName() : historyFile.fileName());
+ return;
+ }
+
+ QDataStream out(saveAll ? &tempFile : &historyFile);
+ for (int i = first; i >= 0; --i) {
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ HistoryItem item = m_history.at(i);
+ stream << HISTORY_VERSION << item.url << item.dateTime << item.title;
+ out << data;
+ }
+ tempFile.close();
+
+ if (saveAll) {
+ if (historyFile.exists() && !historyFile.remove())
+ qWarning() << "History: error removing old history." << historyFile.errorString();
+ if (!tempFile.rename(historyFile.fileName()))
+ qWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName();
+ }
+ m_lastSavedUrl = m_history.value(0).url;
+}
+
+HistoryModel::HistoryModel(HistoryManager *history, QObject *parent)
+ : QAbstractTableModel(parent)
+ , m_history(history)
+{
+ Q_ASSERT(m_history);
+ connect(m_history, SIGNAL(historyReset()),
+ this, SLOT(historyReset()));
+ connect(m_history, SIGNAL(entryRemoved(const HistoryItem &)),
+ this, SLOT(historyReset()));
+
+ connect(m_history, SIGNAL(entryAdded(const HistoryItem &)),
+ this, SLOT(entryAdded()));
+ connect(m_history, SIGNAL(entryUpdated(int)),
+ this, SLOT(entryUpdated(int)));
+}
+
+void HistoryModel::historyReset()
+{
+ reset();
+}
+
+void HistoryModel::entryAdded()
+{
+ beginInsertRows(QModelIndex(), 0, 0);
+ endInsertRows();
+}
+
+void HistoryModel::entryUpdated(int offset)
+{
+ QModelIndex idx = index(offset, 0);
+ emit dataChanged(idx, idx);
+}
+
+QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal
+ && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0: return tr("Title");
+ case 1: return tr("Address");
+ }
+ }
+ return QAbstractTableModel::headerData(section, orientation, role);
+}
+
+QVariant HistoryModel::data(const QModelIndex &index, int role) const
+{
+ QList<HistoryItem> lst = m_history->history();
+ if (index.row() < 0 || index.row() >= lst.size())
+ return QVariant();
+
+ const HistoryItem &item = lst.at(index.row());
+ switch (role) {
+ case DateTimeRole:
+ return item.dateTime;
+ case DateRole:
+ return item.dateTime.date();
+ case UrlRole:
+ return QUrl(item.url);
+ case UrlStringRole:
+ return item.url;
+ case Qt::DisplayRole:
+ case Qt::EditRole: {
+ switch (index.column()) {
+ case 0:
+ // when there is no title try to generate one from the url
+ if (item.title.isEmpty()) {
+ QString page = QFileInfo(QUrl(item.url).path()).fileName();
+ if (!page.isEmpty())
+ return page;
+ return item.url;
+ }
+ return item.title;
+ case 1:
+ return item.url;
+ }
+ }
+ case Qt::DecorationRole:
+ if (index.column() == 0) {
+ return BrowserApplication::instance()->icon(item.url);
+ }
+ }
+ return QVariant();
+}
+
+int HistoryModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : 2;
+}
+
+int HistoryModel::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : m_history->history().count();
+}
+
+bool HistoryModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (parent.isValid())
+ return false;
+ int lastRow = row + count - 1;
+ beginRemoveRows(parent, row, lastRow);
+ QList<HistoryItem> lst = m_history->history();
+ for (int i = lastRow; i >= row; --i)
+ lst.removeAt(i);
+ disconnect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset()));
+ m_history->setHistory(lst);
+ connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset()));
+ endRemoveRows();
+ return true;
+}
+
+#define MOVEDROWS 15
+
+/*
+ Maps the first bunch of items of the source model to the root
+*/
+HistoryMenuModel::HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent)
+ : QAbstractProxyModel(parent)
+ , m_treeModel(sourceModel)
+{
+ setSourceModel(sourceModel);
+}
+
+int HistoryMenuModel::bumpedRows() const
+{
+ QModelIndex first = m_treeModel->index(0, 0);
+ if (!first.isValid())
+ return 0;
+ return qMin(m_treeModel->rowCount(first), MOVEDROWS);
+}
+
+int HistoryMenuModel::columnCount(const QModelIndex &parent) const
+{
+ return m_treeModel->columnCount(mapToSource(parent));
+}
+
+int HistoryMenuModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() > 0)
+ return 0;
+
+ if (!parent.isValid()) {
+ int folders = sourceModel()->rowCount();
+ int bumpedItems = bumpedRows();
+ if (bumpedItems <= MOVEDROWS
+ && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0)))
+ --folders;
+ return bumpedItems + folders;
+ }
+
+ if (parent.internalId() == -1) {
+ if (parent.row() < bumpedRows())
+ return 0;
+ }
+
+ QModelIndex idx = mapToSource(parent);
+ int defaultCount = sourceModel()->rowCount(idx);
+ if (idx == sourceModel()->index(0, 0))
+ return defaultCount - bumpedRows();
+ return defaultCount;
+}
+
+QModelIndex HistoryMenuModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ // currently not used or autotested
+ Q_ASSERT(false);
+ int sr = m_treeModel->mapToSource(sourceIndex).row();
+ return createIndex(sourceIndex.row(), sourceIndex.column(), sr);
+}
+
+QModelIndex HistoryMenuModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ if (!proxyIndex.isValid())
+ return QModelIndex();
+
+ if (proxyIndex.internalId() == -1) {
+ int bumpedItems = bumpedRows();
+ if (proxyIndex.row() < bumpedItems)
+ return m_treeModel->index(proxyIndex.row(), proxyIndex.column(), m_treeModel->index(0, 0));
+ if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(m_treeModel->index(0, 0)))
+ --bumpedItems;
+ return m_treeModel->index(proxyIndex.row() - bumpedItems, proxyIndex.column());
+ }
+
+ QModelIndex historyIndex = m_treeModel->sourceModel()->index(proxyIndex.internalId(), proxyIndex.column());
+ QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex);
+ return treeIndex;
+}
+
+QModelIndex HistoryMenuModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (row < 0
+ || column < 0 || column >= columnCount(parent)
+ || parent.column() > 0)
+ return QModelIndex();
+ if (!parent.isValid())
+ return createIndex(row, column, -1);
+
+ QModelIndex treeIndexParent = mapToSource(parent);
+
+ int bumpedItems = 0;
+ if (treeIndexParent == m_treeModel->index(0, 0))
+ bumpedItems = bumpedRows();
+ QModelIndex treeIndex = m_treeModel->index(row + bumpedItems, column, treeIndexParent);
+ QModelIndex historyIndex = m_treeModel->mapToSource(treeIndex);
+ int historyRow = historyIndex.row();
+ if (historyRow == -1)
+ historyRow = treeIndex.row();
+ return createIndex(row, column, historyRow);
+}
+
+QModelIndex HistoryMenuModel::parent(const QModelIndex &index) const
+{
+ int offset = index.internalId();
+ if (offset == -1 || !index.isValid())
+ return QModelIndex();
+
+ QModelIndex historyIndex = m_treeModel->sourceModel()->index(index.internalId(), 0);
+ QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex);
+ QModelIndex treeIndexParent = treeIndex.parent();
+
+ int sr = m_treeModel->mapToSource(treeIndexParent).row();
+ int bumpedItems = bumpedRows();
+ if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0)))
+ --bumpedItems;
+ return createIndex(bumpedItems + treeIndexParent.row(), treeIndexParent.column(), sr);
+}
+
+
+HistoryMenu::HistoryMenu(QWidget *parent)
+ : ModelMenu(parent)
+ , m_history(0)
+{
+ connect(this, SIGNAL(activated(const QModelIndex &)),
+ this, SLOT(activated(const QModelIndex &)));
+ setHoverRole(HistoryModel::UrlStringRole);
+}
+
+void HistoryMenu::activated(const QModelIndex &index)
+{
+ emit openUrl(index.data(HistoryModel::UrlRole).toUrl());
+}
+
+bool HistoryMenu::prePopulated()
+{
+ if (!m_history) {
+ m_history = BrowserApplication::historyManager();
+ m_historyMenuModel = new HistoryMenuModel(m_history->historyTreeModel(), this);
+ setModel(m_historyMenuModel);
+ }
+ // initial actions
+ for (int i = 0; i < m_initialActions.count(); ++i)
+ addAction(m_initialActions.at(i));
+ if (!m_initialActions.isEmpty())
+ addSeparator();
+ setFirstSeparator(m_historyMenuModel->bumpedRows());
+
+ return false;
+}
+
+void HistoryMenu::postPopulated()
+{
+ if (m_history->history().count() > 0)
+ addSeparator();
+
+ QAction *showAllAction = new QAction(tr("Show All History"), this);
+ connect(showAllAction, SIGNAL(triggered()), this, SLOT(showHistoryDialog()));
+ addAction(showAllAction);
+
+ QAction *clearAction = new QAction(tr("Clear History"), this);
+ connect(clearAction, SIGNAL(triggered()), m_history, SLOT(clear()));
+ addAction(clearAction);
+}
+
+void HistoryMenu::showHistoryDialog()
+{
+ HistoryDialog *dialog = new HistoryDialog(this);
+ connect(dialog, SIGNAL(openUrl(const QUrl&)),
+ this, SIGNAL(openUrl(const QUrl&)));
+ dialog->show();
+}
+
+void HistoryMenu::setInitialActions(QList<QAction*> actions)
+{
+ m_initialActions = actions;
+ for (int i = 0; i < m_initialActions.count(); ++i)
+ addAction(m_initialActions.at(i));
+}
+
+TreeProxyModel::TreeProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
+{
+ setSortRole(HistoryModel::DateTimeRole);
+ setFilterCaseSensitivity(Qt::CaseInsensitive);
+}
+
+bool TreeProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ if (!source_parent.isValid())
+ return true;
+ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+}
+
+HistoryDialog::HistoryDialog(QWidget *parent, HistoryManager *setHistory) : QDialog(parent)
+{
+ HistoryManager *history = setHistory;
+ if (!history)
+ history = BrowserApplication::historyManager();
+ setupUi(this);
+ tree->setUniformRowHeights(true);
+ tree->setSelectionBehavior(QAbstractItemView::SelectRows);
+ tree->setTextElideMode(Qt::ElideMiddle);
+ QAbstractItemModel *model = history->historyTreeModel();
+ TreeProxyModel *proxyModel = new TreeProxyModel(this);
+ connect(search, SIGNAL(textChanged(QString)),
+ proxyModel, SLOT(setFilterFixedString(QString)));
+ connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne()));
+ connect(removeAllButton, SIGNAL(clicked()), history, SLOT(clear()));
+ proxyModel->setSourceModel(model);
+ tree->setModel(proxyModel);
+ tree->setExpanded(proxyModel->index(0, 0), true);
+ tree->setAlternatingRowColors(true);
+ QFontMetrics fm(font());
+ int header = fm.width(QLatin1Char('m')) * 40;
+ tree->header()->resizeSection(0, header);
+ tree->header()->setStretchLastSection(true);
+ connect(tree, SIGNAL(activated(const QModelIndex&)),
+ this, SLOT(open()));
+ tree->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(customContextMenuRequested(const QPoint &)));
+}
+
+void HistoryDialog::customContextMenuRequested(const QPoint &pos)
+{
+ QMenu menu;
+ QModelIndex index = tree->indexAt(pos);
+ index = index.sibling(index.row(), 0);
+ if (index.isValid() && !tree->model()->hasChildren(index)) {
+ menu.addAction(tr("Open"), this, SLOT(open()));
+ menu.addSeparator();
+ menu.addAction(tr("Copy"), this, SLOT(copy()));
+ }
+ menu.addAction(tr("Delete"), tree, SLOT(removeOne()));
+ menu.exec(QCursor::pos());
+}
+
+void HistoryDialog::open()
+{
+ QModelIndex index = tree->currentIndex();
+ if (!index.parent().isValid())
+ return;
+ emit openUrl(index.data(HistoryModel::UrlRole).toUrl());
+}
+
+void HistoryDialog::copy()
+{
+ QModelIndex index = tree->currentIndex();
+ if (!index.parent().isValid())
+ return;
+ QString url = index.data(HistoryModel::UrlStringRole).toString();
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(url);
+}
+
+HistoryFilterModel::HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent)
+ : QAbstractProxyModel(parent),
+ m_loaded(false)
+{
+ setSourceModel(sourceModel);
+}
+
+int HistoryFilterModel::historyLocation(const QString &url) const
+{
+ load();
+ if (!m_historyHash.contains(url))
+ return 0;
+ return sourceModel()->rowCount() - m_historyHash.value(url);
+}
+
+QVariant HistoryFilterModel::data(const QModelIndex &index, int role) const
+{
+ return QAbstractProxyModel::data(index, role);
+}
+
+void HistoryFilterModel::setSourceModel(QAbstractItemModel *newSourceModel)
+{
+ if (sourceModel()) {
+ disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+ disconnect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
+ disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsInserted(const QModelIndex &, int, int)));
+ disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
+ }
+
+ QAbstractProxyModel::setSourceModel(newSourceModel);
+
+ if (sourceModel()) {
+ m_loaded = false;
+ connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+ connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+ this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &)));
+ connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsInserted(const QModelIndex &, int, int)));
+ connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
+ }
+}
+
+void HistoryFilterModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight));
+}
+
+QVariant HistoryFilterModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ return sourceModel()->headerData(section, orientation, role);
+}
+
+void HistoryFilterModel::sourceReset()
+{
+ m_loaded = false;
+ reset();
+}
+
+int HistoryFilterModel::rowCount(const QModelIndex &parent) const
+{
+ load();
+ if (parent.isValid())
+ return 0;
+ return m_historyHash.count();
+}
+
+int HistoryFilterModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : 2;
+}
+
+QModelIndex HistoryFilterModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ load();
+ int sourceRow = sourceModel()->rowCount() - proxyIndex.internalId();
+ return sourceModel()->index(sourceRow, proxyIndex.column());
+}
+
+QModelIndex HistoryFilterModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ load();
+ QString url = sourceIndex.data(HistoryModel::UrlStringRole).toString();
+ if (!m_historyHash.contains(url))
+ return QModelIndex();
+
+ // This can be done in a binary search, but we can't use qBinary find
+ // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v);
+ // so if this is a performance bottlneck then convert to binary search, until then
+ // the cleaner/easier to read code wins the day.
+ int realRow = -1;
+ int sourceModelRow = sourceModel()->rowCount() - sourceIndex.row();
+
+ for (int i = 0; i < m_sourceRow.count(); ++i) {
+ if (m_sourceRow.at(i) == sourceModelRow) {
+ realRow = i;
+ break;
+ }
+ }
+ if (realRow == -1)
+ return QModelIndex();
+
+ return createIndex(realRow, sourceIndex.column(), sourceModel()->rowCount() - sourceIndex.row());
+}
+
+QModelIndex HistoryFilterModel::index(int row, int column, const QModelIndex &parent) const
+{
+ load();
+ if (row < 0 || row >= rowCount(parent)
+ || column < 0 || column >= columnCount(parent))
+ return QModelIndex();
+
+ return createIndex(row, column, m_sourceRow[row]);
+}
+
+QModelIndex HistoryFilterModel::parent(const QModelIndex &) const
+{
+ return QModelIndex();
+}
+
+void HistoryFilterModel::load() const
+{
+ if (m_loaded)
+ return;
+ m_sourceRow.clear();
+ m_historyHash.clear();
+ m_historyHash.reserve(sourceModel()->rowCount());
+ for (int i = 0; i < sourceModel()->rowCount(); ++i) {
+ QModelIndex idx = sourceModel()->index(i, 0);
+ QString url = idx.data(HistoryModel::UrlStringRole).toString();
+ if (!m_historyHash.contains(url)) {
+ m_sourceRow.append(sourceModel()->rowCount() - i);
+ m_historyHash[url] = sourceModel()->rowCount() - i;
+ }
+ }
+ m_loaded = true;
+}
+
+void HistoryFilterModel::sourceRowsInserted(const QModelIndex &parent, int start, int end)
+{
+ Q_ASSERT(start == end && start == 0);
+ Q_UNUSED(end);
+ if (!m_loaded)
+ return;
+ QModelIndex idx = sourceModel()->index(start, 0, parent);
+ QString url = idx.data(HistoryModel::UrlStringRole).toString();
+ if (m_historyHash.contains(url)) {
+ int sourceRow = sourceModel()->rowCount() - m_historyHash[url];
+ int realRow = mapFromSource(sourceModel()->index(sourceRow, 0)).row();
+ beginRemoveRows(QModelIndex(), realRow, realRow);
+ m_sourceRow.removeAt(realRow);
+ m_historyHash.remove(url);
+ endRemoveRows();
+ }
+ beginInsertRows(QModelIndex(), 0, 0);
+ m_historyHash.insert(url, sourceModel()->rowCount() - start);
+ m_sourceRow.insert(0, sourceModel()->rowCount());
+ endInsertRows();
+}
+
+void HistoryFilterModel::sourceRowsRemoved(const QModelIndex &, int start, int end)
+{
+ Q_UNUSED(start);
+ Q_UNUSED(end);
+ sourceReset();
+}
+
+/*
+ Removing a continuous block of rows will remove filtered rows too as this is
+ the users intention.
+*/
+bool HistoryFilterModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid())
+ return false;
+ int lastRow = row + count - 1;
+ disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
+ beginRemoveRows(parent, row, lastRow);
+ int oldCount = rowCount();
+ int start = sourceModel()->rowCount() - m_sourceRow.value(row);
+ int end = sourceModel()->rowCount() - m_sourceRow.value(lastRow);
+ sourceModel()->removeRows(start, end - start + 1);
+ endRemoveRows();
+ connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
+ m_loaded = false;
+ if (oldCount - count != rowCount())
+ reset();
+ return true;
+}
+
+HistoryCompletionModel::HistoryCompletionModel(QObject *parent)
+ : QAbstractProxyModel(parent)
+{
+}
+
+QVariant HistoryCompletionModel::data(const QModelIndex &index, int role) const
+{
+ if (sourceModel()
+ && (role == Qt::EditRole || role == Qt::DisplayRole)
+ && index.isValid()) {
+ QModelIndex idx = mapToSource(index);
+ idx = idx.sibling(idx.row(), 1);
+ QString urlString = idx.data(HistoryModel::UrlStringRole).toString();
+ if (index.row() % 2) {
+ QUrl url = urlString;
+ QString s = url.toString(QUrl::RemoveScheme
+ | QUrl::RemoveUserInfo
+ | QUrl::StripTrailingSlash);
+ return s.mid(2); // strip // from the front
+ }
+ return urlString;
+ }
+ return QAbstractProxyModel::data(index, role);
+}
+
+int HistoryCompletionModel::rowCount(const QModelIndex &parent) const
+{
+ return (parent.isValid() || !sourceModel()) ? 0 : sourceModel()->rowCount(parent) * 2;
+}
+
+int HistoryCompletionModel::columnCount(const QModelIndex &parent) const
+{
+ return (parent.isValid()) ? 0 : 1;
+}
+
+QModelIndex HistoryCompletionModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ int row = sourceIndex.row() * 2;
+ return index(row, sourceIndex.column());
+}
+
+QModelIndex HistoryCompletionModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ if (!sourceModel())
+ return QModelIndex();
+ int row = proxyIndex.row() / 2;
+ return sourceModel()->index(row, proxyIndex.column());
+}
+
+QModelIndex HistoryCompletionModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (row < 0 || row >= rowCount(parent)
+ || column < 0 || column >= columnCount(parent))
+ return QModelIndex();
+ return createIndex(row, column, 0);
+}
+
+QModelIndex HistoryCompletionModel::parent(const QModelIndex &) const
+{
+ return QModelIndex();
+}
+
+void HistoryCompletionModel::setSourceModel(QAbstractItemModel *newSourceModel)
+{
+ if (sourceModel()) {
+ disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+ disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(sourceReset()));
+ disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceReset()));
+ }
+
+ QAbstractProxyModel::setSourceModel(newSourceModel);
+
+ if (newSourceModel) {
+ connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset()));
+ connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(sourceReset()));
+ connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceReset()));
+ }
+
+ reset();
+}
+
+void HistoryCompletionModel::sourceReset()
+{
+ reset();
+}
+
+HistoryTreeModel::HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent)
+ : QAbstractProxyModel(parent)
+{
+ setSourceModel(sourceModel);
+}
+
+QVariant HistoryTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ return sourceModel()->headerData(section, orientation, role);
+}
+
+QVariant HistoryTreeModel::data(const QModelIndex &index, int role) const
+{
+ if ((role == Qt::EditRole || role == Qt::DisplayRole)) {
+ int start = index.internalId();
+ if (start == 0) {
+ int offset = sourceDateRow(index.row());
+ if (index.column() == 0) {
+ QModelIndex idx = sourceModel()->index(offset, 0);
+ QDate date = idx.data(HistoryModel::DateRole).toDate();
+ if (date == QDate::currentDate())
+ return tr("Earlier Today");
+ return date.toString(QLatin1String("dddd, MMMM d, yyyy"));
+ }
+ if (index.column() == 1) {
+ return tr("%1 items").arg(rowCount(index.sibling(index.row(), 0)));
+ }
+ }
+ }
+ if (role == Qt::DecorationRole && index.column() == 0 && !index.parent().isValid())
+ return QIcon(QLatin1String(":history.png"));
+ if (role == HistoryModel::DateRole && index.column() == 0 && index.internalId() == 0) {
+ int offset = sourceDateRow(index.row());
+ QModelIndex idx = sourceModel()->index(offset, 0);
+ return idx.data(HistoryModel::DateRole);
+ }
+
+ return QAbstractProxyModel::data(index, role);
+}
+
+int HistoryTreeModel::columnCount(const QModelIndex &parent) const
+{
+ return sourceModel()->columnCount(mapToSource(parent));
+}
+
+int HistoryTreeModel::rowCount(const QModelIndex &parent) const
+{
+ if ( parent.internalId() != 0
+ || parent.column() > 0
+ || !sourceModel())
+ return 0;
+
+ // row count OF dates
+ if (!parent.isValid()) {
+ if (!m_sourceRowCache.isEmpty())
+ return m_sourceRowCache.count();
+ QDate currentDate;
+ int rows = 0;
+ int totalRows = sourceModel()->rowCount();
+
+ for (int i = 0; i < totalRows; ++i) {
+ QDate rowDate = sourceModel()->index(i, 0).data(HistoryModel::DateRole).toDate();
+ if (rowDate != currentDate) {
+ m_sourceRowCache.append(i);
+ currentDate = rowDate;
+ ++rows;
+ }
+ }
+ Q_ASSERT(m_sourceRowCache.count() == rows);
+ return rows;
+ }
+
+ // row count FOR a date
+ int start = sourceDateRow(parent.row());
+ int end = sourceDateRow(parent.row() + 1);
+ return (end - start);
+}
+
+// Translate the top level date row into the offset where that date starts
+int HistoryTreeModel::sourceDateRow(int row) const
+{
+ if (row <= 0)
+ return 0;
+
+ if (m_sourceRowCache.isEmpty())
+ rowCount(QModelIndex());
+
+ if (row >= m_sourceRowCache.count()) {
+ if (!sourceModel())
+ return 0;
+ return sourceModel()->rowCount();
+ }
+ return m_sourceRowCache.at(row);
+}
+
+QModelIndex HistoryTreeModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ int offset = proxyIndex.internalId();
+ if (offset == 0)
+ return QModelIndex();
+ int startDateRow = sourceDateRow(offset - 1);
+ return sourceModel()->index(startDateRow + proxyIndex.row(), proxyIndex.column());
+}
+
+QModelIndex HistoryTreeModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (row < 0
+ || column < 0 || column >= columnCount(parent)
+ || parent.column() > 0)
+ return QModelIndex();
+
+ if (!parent.isValid())
+ return createIndex(row, column, 0);
+ return createIndex(row, column, parent.row() + 1);
+}
+
+QModelIndex HistoryTreeModel::parent(const QModelIndex &index) const
+{
+ int offset = index.internalId();
+ if (offset == 0 || !index.isValid())
+ return QModelIndex();
+ return createIndex(offset - 1, 0, 0);
+}
+
+bool HistoryTreeModel::hasChildren(const QModelIndex &parent) const
+{
+ QModelIndex grandparent = parent.parent();
+ if (!grandparent.isValid())
+ return true;
+ return false;
+}
+
+Qt::ItemFlags HistoryTreeModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
+}
+
+bool HistoryTreeModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || count <= 0 || row + count > rowCount(parent))
+ return false;
+
+ if (parent.isValid()) {
+ // removing pages
+ int offset = sourceDateRow(parent.row());
+ return sourceModel()->removeRows(offset + row, count);
+ } else {
+ // removing whole dates
+ for (int i = row + count - 1; i >= row; --i) {
+ QModelIndex dateParent = index(i, 0);
+ int offset = sourceDateRow(dateParent.row());
+ if (!sourceModel()->removeRows(offset, rowCount(dateParent)))
+ return false;
+ }
+ }
+ return true;
+}
+
+void HistoryTreeModel::setSourceModel(QAbstractItemModel *newSourceModel)
+{
+ if (sourceModel()) {
+ disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+ disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset()));
+ disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsInserted(const QModelIndex &, int, int)));
+ disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
+ }
+
+ QAbstractProxyModel::setSourceModel(newSourceModel);
+
+ if (newSourceModel) {
+ connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+ connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset()));
+ connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsInserted(const QModelIndex &, int, int)));
+ connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+ this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int)));
+ }
+
+ reset();
+}
+
+void HistoryTreeModel::sourceReset()
+{
+ m_sourceRowCache.clear();
+ reset();
+}
+
+void HistoryTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end)
+{
+ Q_UNUSED(parent); // Avoid warnings when compiling release
+ Q_ASSERT(!parent.isValid());
+ if (start != 0 || start != end) {
+ m_sourceRowCache.clear();
+ reset();
+ return;
+ }
+
+ m_sourceRowCache.clear();
+ QModelIndex treeIndex = mapFromSource(sourceModel()->index(start, 0));
+ QModelIndex treeParent = treeIndex.parent();
+ if (rowCount(treeParent) == 1) {
+ beginInsertRows(QModelIndex(), 0, 0);
+ endInsertRows();
+ } else {
+ beginInsertRows(treeParent, treeIndex.row(), treeIndex.row());
+ endInsertRows();
+ }
+}
+
+QModelIndex HistoryTreeModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ if (!sourceIndex.isValid())
+ return QModelIndex();
+
+ if (m_sourceRowCache.isEmpty())
+ rowCount(QModelIndex());
+
+ QList<int>::iterator it;
+ it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row());
+ if (*it != sourceIndex.row())
+ --it;
+ int dateRow = qMax(0, it - m_sourceRowCache.begin());
+ int row = sourceIndex.row() - m_sourceRowCache.at(dateRow);
+ return createIndex(row, sourceIndex.column(), dateRow + 1);
+}
+
+void HistoryTreeModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_UNUSED(parent); // Avoid warnings when compiling release
+ Q_ASSERT(!parent.isValid());
+ if (m_sourceRowCache.isEmpty())
+ return;
+ for (int i = end; i >= start;) {
+ QList<int>::iterator it;
+ it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i);
+ // playing it safe
+ if (it == m_sourceRowCache.end()) {
+ m_sourceRowCache.clear();
+ reset();
+ return;
+ }
+
+ if (*it != i)
+ --it;
+ int row = qMax(0, it - m_sourceRowCache.begin());
+ int offset = m_sourceRowCache[row];
+ QModelIndex dateParent = index(row, 0);
+ // If we can remove all the rows in the date do that and skip over them
+ int rc = rowCount(dateParent);
+ if (i - rc + 1 == offset && start <= i - rc + 1) {
+ beginRemoveRows(QModelIndex(), row, row);
+ m_sourceRowCache.removeAt(row);
+ i -= rc + 1;
+ } else {
+ beginRemoveRows(dateParent, i - offset, i - offset);
+ ++row;
+ --i;
+ }
+ for (int j = row; j < m_sourceRowCache.count(); ++j)
+ --m_sourceRowCache[j];
+ endRemoveRows();
+ }
+}
diff --git a/src/history.h b/src/history.h
new file mode 100644
index 00000000..cda78a5a
--- /dev/null
+++ b/src/history.h
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef HISTORY_H
+#define HISTORY_H
+
+#include "modelmenu.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+#include <QtCore/QTimer>
+#include <QtCore/QUrl>
+
+#include <QtGui/QSortFilterProxyModel>
+
+#include <QWebHistoryInterface>
+
+class HistoryItem
+{
+public:
+ HistoryItem() {}
+ HistoryItem(const QString &u,
+ const QDateTime &d = QDateTime(), const QString &t = QString())
+ : title(t), url(u), dateTime(d) {}
+
+ inline bool operator==(const HistoryItem &other) const
+ { return other.title == title
+ && other.url == url && other.dateTime == dateTime; }
+
+ // history is sorted in reverse
+ inline bool operator <(const HistoryItem &other) const
+ { return dateTime > other.dateTime; }
+
+ QString title;
+ QString url;
+ QDateTime dateTime;
+};
+
+class AutoSaver;
+class HistoryModel;
+class HistoryFilterModel;
+class HistoryTreeModel;
+class HistoryManager : public QWebHistoryInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit)
+
+signals:
+ void historyReset();
+ void entryAdded(const HistoryItem &item);
+ void entryRemoved(const HistoryItem &item);
+ void entryUpdated(int offset);
+
+public:
+ HistoryManager(QObject *parent = 0);
+ ~HistoryManager();
+
+ bool historyContains(const QString &url) const;
+ void addHistoryEntry(const QString &url);
+
+ void updateHistoryItem(const QUrl &url, const QString &title);
+
+ int historyLimit() const;
+ void setHistoryLimit(int limit);
+
+ QList<HistoryItem> history() const;
+ void setHistory(const QList<HistoryItem> &history, bool loadedAndSorted = false);
+
+ // History manager keeps around these models for use by the completer and other classes
+ HistoryModel *historyModel() const;
+ HistoryFilterModel *historyFilterModel() const;
+ HistoryTreeModel *historyTreeModel() const;
+
+public slots:
+ void clear();
+ void loadSettings();
+
+private slots:
+ void save();
+ void checkForExpired();
+
+protected:
+ void addHistoryItem(const HistoryItem &item);
+
+private:
+ void load();
+
+ AutoSaver *m_saveTimer;
+ int m_historyLimit;
+ QTimer m_expiredTimer;
+ QList<HistoryItem> m_history;
+ QString m_lastSavedUrl;
+
+ HistoryModel *m_historyModel;
+ HistoryFilterModel *m_historyFilterModel;
+ HistoryTreeModel *m_historyTreeModel;
+};
+
+class HistoryModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public slots:
+ void historyReset();
+ void entryAdded();
+ void entryUpdated(int offset);
+
+public:
+ enum Roles {
+ DateRole = Qt::UserRole + 1,
+ DateTimeRole = Qt::UserRole + 2,
+ UrlRole = Qt::UserRole + 3,
+ UrlStringRole = Qt::UserRole + 4
+ };
+
+ HistoryModel(HistoryManager *history, QObject *parent = 0);
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+private:
+ HistoryManager *m_history;
+};
+
+/*!
+ Proxy model that will remove any duplicate entries.
+ Both m_sourceRow and m_historyHash store their offsets not from
+ the front of the list, but as offsets from the back.
+ */
+class HistoryFilterModel : public QAbstractProxyModel
+{
+ Q_OBJECT
+
+public:
+ HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent = 0);
+
+ inline bool historyContains(const QString &url) const
+ { load(); return m_historyHash.contains(url); }
+ int historyLocation(const QString &url) const;
+
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+ void setSourceModel(QAbstractItemModel *sourceModel);
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex& index= QModelIndex()) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+private slots:
+ void sourceReset();
+ void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void sourceRowsInserted(const QModelIndex &parent, int start, int end);
+ void sourceRowsRemoved(const QModelIndex &, int, int);
+
+private:
+ void load() const;
+
+ mutable QList<int> m_sourceRow;
+ mutable QHash<QString, int> m_historyHash;
+ mutable bool m_loaded;
+};
+
+/*
+ The history menu
+ - Removes the first twenty entries and puts them as children of the top level.
+ - If there are less then twenty entries then the first folder is also removed.
+
+ The mapping is done by knowing that HistoryTreeModel is over a table
+ We store that row offset in our index's private data.
+*/
+class HistoryMenuModel : public QAbstractProxyModel
+{
+ Q_OBJECT
+
+public:
+ HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = 0);
+ int columnCount(const QModelIndex &parent) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex mapFromSource(const QModelIndex & sourceIndex) const;
+ QModelIndex mapToSource(const QModelIndex & proxyIndex) const;
+ QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &index = QModelIndex()) const;
+
+ int bumpedRows() const;
+
+private:
+ HistoryTreeModel *m_treeModel;
+};
+
+// Menu that is dynamically populated from the history
+class HistoryMenu : public ModelMenu
+{
+ Q_OBJECT
+
+signals:
+ void openUrl(const QUrl &url);
+
+public:
+ HistoryMenu(QWidget *parent = 0);
+ void setInitialActions(QList<QAction*> actions);
+
+protected:
+ bool prePopulated();
+ void postPopulated();
+
+private slots:
+ void activated(const QModelIndex &index);
+ void showHistoryDialog();
+
+private:
+ HistoryManager *m_history;
+ HistoryMenuModel *m_historyMenuModel;
+ QList<QAction*> m_initialActions;
+};
+
+// proxy model for the history model that
+// exposes each url http://www.foo.com and it url starting at the host www.foo.com
+class HistoryCompletionModel : public QAbstractProxyModel
+{
+ Q_OBJECT
+
+public:
+ HistoryCompletionModel(QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+ QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex& index= QModelIndex()) const;
+ void setSourceModel(QAbstractItemModel *sourceModel);
+
+private slots:
+ void sourceReset();
+
+};
+
+// proxy model for the history model that converts the list
+// into a tree, one top level node per day.
+// Used in the HistoryDialog.
+class HistoryTreeModel : public QAbstractProxyModel
+{
+ Q_OBJECT
+
+public:
+ HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ int columnCount(const QModelIndex &parent) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &index= QModelIndex()) const;
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+ void setSourceModel(QAbstractItemModel *sourceModel);
+
+private slots:
+ void sourceReset();
+ void sourceRowsInserted(const QModelIndex &parent, int start, int end);
+ void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
+
+private:
+ int sourceDateRow(int row) const;
+ mutable QList<int> m_sourceRowCache;
+
+};
+
+// A modified QSortFilterProxyModel that always accepts the root nodes in the tree
+// so filtering is only done on the children.
+// Used in the HistoryDialog
+class TreeProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ TreeProxyModel(QObject *parent = 0);
+
+protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+};
+
+#include "ui_history.h"
+
+class HistoryDialog : public QDialog, public Ui_HistoryDialog
+{
+ Q_OBJECT
+
+signals:
+ void openUrl(const QUrl &url);
+
+public:
+ HistoryDialog(QWidget *parent = 0, HistoryManager *history = 0);
+
+private slots:
+ void customContextMenuRequested(const QPoint &pos);
+ void open();
+ void copy();
+
+};
+
+#endif // HISTORY_H
+
diff --git a/src/history.ui b/src/history.ui
new file mode 100644
index 00000000..0944940e
--- /dev/null
+++ b/src/history.ui
@@ -0,0 +1,106 @@
+<ui version="4.0" >
+ <class>HistoryDialog</class>
+ <widget class="QDialog" name="HistoryDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>758</width>
+ <height>450</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>History</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>252</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1" >
+ <widget class="SearchLineEdit" name="search" />
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="EditTreeView" name="tree" />
+ </item>
+ <item row="2" column="0" colspan="2" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QPushButton" name="removeButton" >
+ <property name="text" >
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeAllButton" >
+ <property name="text" >
+ <string>Remove &amp;All</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SearchLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>searchlineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>EditTreeView</class>
+ <extends>QTreeView</extends>
+ <header>edittreeview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>HistoryDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>472</x>
+ <y>329</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>461</x>
+ <y>356</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/htmls/htmls.qrc b/src/htmls/htmls.qrc
new file mode 100644
index 00000000..03b256cc
--- /dev/null
+++ b/src/htmls/htmls.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>notfound.html</file>
+</qresource>
+</RCC>
diff --git a/src/htmls/notfound.html b/src/htmls/notfound.html
new file mode 100755
index 00000000..b428a6a3
--- /dev/null
+++ b/src/htmls/notfound.html
@@ -0,0 +1,63 @@
+<html>
+<head>
+<title>%1</title>
+<style>
+body {
+ padding: 3em 0em;
+ background: #eeeeee;
+}
+hr {
+ color: lightgray;
+ width: 100%;
+}
+img {
+ float: left;
+ opacity: .8;
+}
+#box {
+ background: white;
+ border: 1px solid lightgray;
+ width: 600px;
+ padding: 60px;
+ margin: auto;
+}
+h1 {
+ font-size: 130%;
+ font-weight: bold;
+ border-bottom: 1px solid lightgray;
+ margin-left: 48px;
+}
+h2 {
+ font-size: 100%;
+ font-weight: normal;
+ border-bottom: 1px solid lightgray;
+ margin-left: 48px;
+}
+ul {
+ font-size: 80%;
+ padding-left: 48px;
+ margin: 0;
+}
+#reloadButton {
+ padding-left: 48px;
+}
+</style>
+</head>
+<body>
+ <div id="box">
+ <img src="data:image/png;base64,IMAGE_BINARY_DATA_HERE" width="32" height="32"/>
+ <h1>%2</h1>
+ <h2>When connecting to: %3.</h2>
+ <ul>
+ <li>Check the address for errors such as <b>ww</b>.trolltech.com
+ instead of <b>www</b>.trolltech.com</li>
+ <li>If the address is correct, try to check the network
+ connection.</li>
+ <li>If your computer or network is protected by a firewall or
+ proxy, make sure that the browser demo is permitted to access
+ the network.</li>
+ </ul>
+ <br/><br/>
+ </div>
+</body>
+</html>
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 00000000..ae4c2173
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,57 @@
+/* ============================================================
+ *
+ * This file is a part of the reKonq project
+ *
+ * Copyright (C) 2008 by Andrea Diamantini <adjam7 at gmail dot 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 2, 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.
+ *
+ * ============================================================ */
+
+
+#include <KAboutData>
+#include <KCmdLineArgs>
+
+#include "browserapplication.h"
+
+int main(int argc, char **argv)
+{
+ KAboutData aboutData(
+ "reKonq",
+ 0,
+ ki18n("reKonq"),
+ "0.0.1",
+ ki18n("A KDE browser webkit based"),
+ KAboutData::License_GPL,
+ ki18n("Copyright (c) 2008 Andrea Diamantini"),
+ KLocalizedString(),
+ "http://www.adjam.org",
+ "adjam7@gmail.com" // bug report mail
+ );
+
+ aboutData.addAuthor(ki18n("Andrea Diamantini"), ki18n("reKonq author"), "adjam7@gmail.com");
+ aboutData.setProgramIconName("application-internet");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineOptions options;
+
+ //TODO make this work
+ options.add("+URL", ki18n("Location to open"));
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ BrowserApplication app(argc, argv);
+ if (!app.isTheOnlyBrowser())
+ return 0;
+ app.newMainWindow();
+ return app.exec();
+}
+
diff --git a/src/modelmenu.cpp b/src/modelmenu.cpp
new file mode 100644
index 00000000..3a8fb0cd
--- /dev/null
+++ b/src/modelmenu.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "modelmenu.h"
+
+#include <QtCore/QAbstractItemModel>
+#include <qdebug.h>
+
+ModelMenu::ModelMenu(QWidget * parent)
+ : QMenu(parent)
+ , m_maxRows(7)
+ , m_firstSeparator(-1)
+ , m_maxWidth(-1)
+ , m_hoverRole(0)
+ , m_separatorRole(0)
+ , m_model(0)
+{
+ connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
+}
+
+bool ModelMenu::prePopulated()
+{
+ return false;
+}
+
+void ModelMenu::postPopulated()
+{
+}
+
+void ModelMenu::setModel(QAbstractItemModel *model)
+{
+ m_model = model;
+}
+
+QAbstractItemModel *ModelMenu::model() const
+{
+ return m_model;
+}
+
+void ModelMenu::setMaxRows(int max)
+{
+ m_maxRows = max;
+}
+
+int ModelMenu::maxRows() const
+{
+ return m_maxRows;
+}
+
+void ModelMenu::setFirstSeparator(int offset)
+{
+ m_firstSeparator = offset;
+}
+
+int ModelMenu::firstSeparator() const
+{
+ return m_firstSeparator;
+}
+
+void ModelMenu::setRootIndex(const QModelIndex &index)
+{
+ m_root = index;
+}
+
+QModelIndex ModelMenu::rootIndex() const
+{
+ return m_root;
+}
+
+void ModelMenu::setHoverRole(int role)
+{
+ m_hoverRole = role;
+}
+
+int ModelMenu::hoverRole() const
+{
+ return m_hoverRole;
+}
+
+void ModelMenu::setSeparatorRole(int role)
+{
+ m_separatorRole = role;
+}
+
+int ModelMenu::separatorRole() const
+{
+ return m_separatorRole;
+}
+
+Q_DECLARE_METATYPE(QModelIndex)
+void ModelMenu::aboutToShow()
+{
+ if (QMenu *menu = qobject_cast<QMenu*>(sender())) {
+ QVariant v = menu->menuAction()->data();
+ if (v.canConvert<QModelIndex>()) {
+ QModelIndex idx = qvariant_cast<QModelIndex>(v);
+ createMenu(idx, -1, menu, menu);
+ disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
+ return;
+ }
+ }
+
+ clear();
+ if (prePopulated())
+ addSeparator();
+ int max = m_maxRows;
+ if (max != -1)
+ max += m_firstSeparator;
+ createMenu(m_root, max, this, this);
+ postPopulated();
+}
+
+void ModelMenu::createMenu(const QModelIndex &parent, int max, QMenu *parentMenu, QMenu *menu)
+{
+ if (!menu) {
+ QString title = parent.data().toString();
+ menu = new QMenu(title, this);
+ QIcon icon = qvariant_cast<QIcon>(parent.data(Qt::DecorationRole));
+ menu->setIcon(icon);
+ parentMenu->addMenu(menu);
+ QVariant v;
+ v.setValue(parent);
+ menu->menuAction()->setData(v);
+ connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
+ return;
+ }
+
+ int end = m_model->rowCount(parent);
+ if (max != -1)
+ end = qMin(max, end);
+
+ connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(triggered(QAction*)));
+ connect(menu, SIGNAL(hovered(QAction*)), this, SLOT(hovered(QAction*)));
+
+ for (int i = 0; i < end; ++i) {
+ QModelIndex idx = m_model->index(i, 0, parent);
+ if (m_model->hasChildren(idx)) {
+ createMenu(idx, -1, menu);
+ } else {
+ if (m_separatorRole != 0
+ && idx.data(m_separatorRole).toBool())
+ addSeparator();
+ else
+ menu->addAction(makeAction(idx));
+ }
+ if (menu == this && i == m_firstSeparator - 1)
+ addSeparator();
+ }
+}
+
+QAction *ModelMenu::makeAction(const QModelIndex &index)
+{
+ QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
+ QAction *action = makeAction(icon, index.data().toString(), this);
+ QVariant v;
+ v.setValue(index);
+ action->setData(v);
+ return action;
+}
+
+QAction *ModelMenu::makeAction(const QIcon &icon, const QString &text, QObject *parent)
+{
+ QFontMetrics fm(font());
+ if (-1 == m_maxWidth)
+ m_maxWidth = fm.width(QLatin1Char('m')) * 30;
+ QString smallText = fm.elidedText(text, Qt::ElideMiddle, m_maxWidth);
+ return new QAction(icon, smallText, parent);
+}
+
+void ModelMenu::triggered(QAction *action)
+{
+ QVariant v = action->data();
+ if (v.canConvert<QModelIndex>()) {
+ QModelIndex idx = qvariant_cast<QModelIndex>(v);
+ emit activated(idx);
+ }
+}
+
+void ModelMenu::hovered(QAction *action)
+{
+ QVariant v = action->data();
+ if (v.canConvert<QModelIndex>()) {
+ QModelIndex idx = qvariant_cast<QModelIndex>(v);
+ QString hoveredString = idx.data(m_hoverRole).toString();
+ if (!hoveredString.isEmpty())
+ emit hovered(hoveredString);
+ }
+}
diff --git a/src/modelmenu.h b/src/modelmenu.h
new file mode 100644
index 00000000..81928256
--- /dev/null
+++ b/src/modelmenu.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef MODELMENU_H
+#define MODELMENU_H
+
+#include <QtGui/QMenu>
+#include <QtCore/QAbstractItemModel>
+
+// A QMenu that is dynamically populated from a QAbstractItemModel
+class ModelMenu : public QMenu
+{
+ Q_OBJECT
+
+signals:
+ void activated(const QModelIndex &index);
+ void hovered(const QString &text);
+
+public:
+ ModelMenu(QWidget *parent = 0);
+
+ void setModel(QAbstractItemModel *model);
+ QAbstractItemModel *model() const;
+
+ void setMaxRows(int max);
+ int maxRows() const;
+
+ void setFirstSeparator(int offset);
+ int firstSeparator() const;
+
+ void setRootIndex(const QModelIndex &index);
+ QModelIndex rootIndex() const;
+
+ void setHoverRole(int role);
+ int hoverRole() const;
+
+ void setSeparatorRole(int role);
+ int separatorRole() const;
+
+ QAction *makeAction(const QIcon &icon, const QString &text, QObject *parent);
+
+protected:
+ // add any actions before the tree, return true if any actions are added.
+ virtual bool prePopulated();
+ // add any actions after the tree
+ virtual void postPopulated();
+ // put all of the children of parent into menu up to max
+ void createMenu(const QModelIndex &parent, int max, QMenu *parentMenu = 0, QMenu *menu = 0);
+
+private slots:
+ void aboutToShow();
+ void triggered(QAction *action);
+ void hovered(QAction *action);
+
+private:
+ QAction *makeAction(const QModelIndex &index);
+ int m_maxRows;
+ int m_firstSeparator;
+ int m_maxWidth;
+ int m_hoverRole;
+ int m_separatorRole;
+ QAbstractItemModel *m_model;
+ QPersistentModelIndex m_root;
+};
+
+#endif // MODELMENU_H
+
diff --git a/src/networkaccessmanager.cpp b/src/networkaccessmanager.cpp
new file mode 100644
index 00000000..8922c8c0
--- /dev/null
+++ b/src/networkaccessmanager.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "networkaccessmanager.h"
+
+#include "browserapplication.h"
+#include "browsermainwindow.h"
+#include "ui_passworddialog.h"
+#include "ui_proxy.h"
+
+#include <QtCore/QSettings>
+
+#include <QtGui/QDialog>
+#include <QtGui/QMessageBox>
+#include <QtGui/QStyle>
+#include <QtGui/QTextDocument>
+
+#include <QtNetwork/QAuthenticator>
+#include <QtNetwork/QNetworkProxy>
+#include <QtNetwork/QNetworkReply>
+#include <QtNetwork/QSslError>
+
+NetworkAccessManager::NetworkAccessManager(QObject *parent)
+ : QNetworkAccessManager(parent)
+{
+ connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
+ SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+ connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
+ SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)));
+#ifndef QT_NO_OPENSSL
+ connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
+ SLOT(sslErrors(QNetworkReply*, const QList<QSslError>&)));
+#endif
+ loadSettings();
+}
+
+void NetworkAccessManager::loadSettings()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("proxy"));
+ QNetworkProxy proxy;
+ if (settings.value(QLatin1String("enabled"), false).toBool()) {
+ if (settings.value(QLatin1String("type"), 0).toInt() == 0)
+ proxy.setType(QNetworkProxy::Socks5Proxy);
+ else
+ proxy.setType(QNetworkProxy::HttpProxy);
+ proxy.setHostName(settings.value(QLatin1String("hostName")).toString());
+ proxy.setPort(settings.value(QLatin1String("port"), 1080).toInt());
+ proxy.setUser(settings.value(QLatin1String("userName")).toString());
+ proxy.setPassword(settings.value(QLatin1String("password")).toString());
+ }
+ setProxy(proxy);
+}
+
+void NetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth)
+{
+ BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow();
+
+ QDialog dialog(mainWindow);
+ dialog.setWindowFlags(Qt::Sheet);
+
+ Ui::PasswordDialog passwordDialog;
+ passwordDialog.setupUi(&dialog);
+
+ passwordDialog.iconLabel->setText(QString());
+ passwordDialog.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32));
+
+ QString introMessage = tr("<qt>Enter username and password for \"%1\" at %2</qt>");
+ introMessage = introMessage.arg(Qt::escape(reply->url().toString())).arg(Qt::escape(reply->url().toString()));
+ passwordDialog.introLabel->setText(introMessage);
+ passwordDialog.introLabel->setWordWrap(true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ auth->setUser(passwordDialog.userNameLineEdit->text());
+ auth->setPassword(passwordDialog.passwordLineEdit->text());
+ }
+}
+
+void NetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth)
+{
+ BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow();
+
+ QDialog dialog(mainWindow);
+ dialog.setWindowFlags(Qt::Sheet);
+
+ Ui::ProxyDialog proxyDialog;
+ proxyDialog.setupUi(&dialog);
+
+ proxyDialog.iconLabel->setText(QString());
+ proxyDialog.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32));
+
+ QString introMessage = tr("<qt>Connect to proxy \"%1\" using:</qt>");
+ introMessage = introMessage.arg(Qt::escape(proxy.hostName()));
+ proxyDialog.introLabel->setText(introMessage);
+ proxyDialog.introLabel->setWordWrap(true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ auth->setUser(proxyDialog.userNameLineEdit->text());
+ auth->setPassword(proxyDialog.passwordLineEdit->text());
+ }
+}
+
+#ifndef QT_NO_OPENSSL
+void NetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &error)
+{
+ BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow();
+
+ QStringList errorStrings;
+ for (int i = 0; i < error.count(); ++i)
+ errorStrings += error.at(i).errorString();
+ QString errors = errorStrings.join(QLatin1String("\n"));
+ int ret = QMessageBox::warning(mainWindow, QCoreApplication::applicationName(),
+ tr("SSL Errors:\n\n%1\n\n%2\n\n"
+ "Do you want to ignore these errors?").arg(reply->url().toString()).arg(errors),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No);
+ if (ret == QMessageBox::Yes)
+ reply->ignoreSslErrors();
+}
+#endif
diff --git a/src/networkaccessmanager.h b/src/networkaccessmanager.h
new file mode 100644
index 00000000..d49c7fa4
--- /dev/null
+++ b/src/networkaccessmanager.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef NETWORKACCESSMANAGER_H
+#define NETWORKACCESSMANAGER_H
+
+#include <QtNetwork/QNetworkAccessManager>
+
+class NetworkAccessManager : public QNetworkAccessManager
+{
+ Q_OBJECT
+
+public:
+ NetworkAccessManager(QObject *parent = 0);
+
+public slots:
+ void loadSettings();
+
+private slots:
+ void authenticationRequired(QNetworkReply *reply, QAuthenticator *auth);
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
+#ifndef QT_NO_OPENSSL
+ void sslErrors(QNetworkReply *reply, const QList<QSslError> &error);
+#endif
+};
+
+#endif // NETWORKACCESSMANAGER_H
diff --git a/src/passworddialog.ui b/src/passworddialog.ui
new file mode 100644
index 00000000..7c166586
--- /dev/null
+++ b/src/passworddialog.ui
@@ -0,0 +1,111 @@
+<ui version="4.0" >
+ <class>PasswordDialog</class>
+ <widget class="QDialog" name="PasswordDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>399</width>
+ <height>148</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Authentication Required</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" colspan="2" >
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QLabel" name="iconLabel" >
+ <property name="text" >
+ <string>DUMMY ICON</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="introLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text" >
+ <string>INTRO TEXT DUMMY</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Username:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLineEdit" name="userNameLineEdit" />
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="lblPassword" >
+ <property name="text" >
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLineEdit" name="passwordLineEdit" >
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>PasswordDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>PasswordDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/proxy.ui b/src/proxy.ui
new file mode 100644
index 00000000..62a8be62
--- /dev/null
+++ b/src/proxy.ui
@@ -0,0 +1,104 @@
+<ui version="4.0" >
+ <class>ProxyDialog</class>
+ <widget class="QDialog" name="ProxyDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>369</width>
+ <height>144</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Proxy Authentication</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="iconLabel" >
+ <property name="text" >
+ <string>ICON</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <widget class="QLabel" name="introLabel" >
+ <property name="text" >
+ <string>Connect to proxy</string>
+ </property>
+ <property name="wordWrap" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2" >
+ <widget class="QLabel" name="usernameLabel" >
+ <property name="text" >
+ <string>Username:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QLineEdit" name="userNameLineEdit" />
+ </item>
+ <item row="2" column="0" colspan="2" >
+ <widget class="QLabel" name="passwordLabel" >
+ <property name="text" >
+ <string>Password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <widget class="QLineEdit" name="passwordLineEdit" >
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="3" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ProxyDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ProxyDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/searchlineedit.cpp b/src/searchlineedit.cpp
new file mode 100644
index 00000000..97fdf726
--- /dev/null
+++ b/src/searchlineedit.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "searchlineedit.h"
+
+#include <QtGui/QPainter>
+#include <QtGui/QMouseEvent>
+#include <QtGui/QMenu>
+#include <QtGui/QStyle>
+#include <QtGui/QStyleOptionFrameV2>
+
+ClearButton::ClearButton(QWidget *parent)
+ : QAbstractButton(parent)
+{
+ setCursor(Qt::ArrowCursor);
+ setToolTip(tr("Clear"));
+ setVisible(false);
+ setFocusPolicy(Qt::NoFocus);
+}
+
+void ClearButton::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ QPainter painter(this);
+ int height = this->height();
+
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ QColor color = palette().color(QPalette::Mid);
+ painter.setBrush(isDown()
+ ? palette().color(QPalette::Dark)
+ : palette().color(QPalette::Mid));
+ painter.setPen(painter.brush().color());
+ int size = width();
+ int offset = size / 5;
+ int radius = size - offset * 2;
+ painter.drawEllipse(offset, offset, radius, radius);
+
+ painter.setPen(palette().color(QPalette::Base));
+ int border = offset * 2;
+ painter.drawLine(border, border, width() - border, height - border);
+ painter.drawLine(border, height - border, width() - border, border);
+}
+
+void ClearButton::textChanged(const QString &text)
+{
+ setVisible(!text.isEmpty());
+}
+
+/*
+ Search icon on the left hand side of the search widget
+ When a menu is set a down arrow appears
+ */
+class SearchButton : public QAbstractButton {
+public:
+ SearchButton(QWidget *parent = 0);
+ void paintEvent(QPaintEvent *event);
+ QMenu *m_menu;
+
+protected:
+ void mousePressEvent(QMouseEvent *event);
+};
+
+SearchButton::SearchButton(QWidget *parent)
+ : QAbstractButton(parent),
+ m_menu(0)
+{
+ setObjectName(QLatin1String("SearchButton"));
+ setCursor(Qt::ArrowCursor);
+ setFocusPolicy(Qt::NoFocus);
+}
+
+void SearchButton::mousePressEvent(QMouseEvent *event)
+{
+ if (m_menu && event->button() == Qt::LeftButton) {
+ QWidget *p = parentWidget();
+ if (p) {
+ QPoint r = p->mapToGlobal(QPoint(0, p->height()));
+ m_menu->exec(QPoint(r.x() + height() / 2, r.y()));
+ }
+ event->accept();
+ }
+ QAbstractButton::mousePressEvent(event);
+}
+
+void SearchButton::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+ QPainterPath myPath;
+
+ int radius = (height() / 5) * 2;
+ QRect circle(height() / 3 - 1, height() / 4, radius, radius);
+ myPath.addEllipse(circle);
+
+ myPath.arcMoveTo(circle, 300);
+ QPointF c = myPath.currentPosition();
+ int diff = height() / 7;
+ myPath.lineTo(qMin(width() - 2, (int)c.x() + diff), c.y() + diff);
+
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setPen(QPen(Qt::darkGray, 2));
+ painter.drawPath(myPath);
+
+ if (m_menu) {
+ QPainterPath dropPath;
+ dropPath.arcMoveTo(circle, 320);
+ QPointF c = dropPath.currentPosition();
+ c = QPointF(c.x() + 3.5, c.y() + 0.5);
+ dropPath.moveTo(c);
+ dropPath.lineTo(c.x() + 4, c.y());
+ dropPath.lineTo(c.x() + 2, c.y() + 2);
+ dropPath.closeSubpath();
+ painter.setPen(Qt::darkGray);
+ painter.setBrush(Qt::darkGray);
+ painter.setRenderHint(QPainter::Antialiasing, false);
+ painter.drawPath(dropPath);
+ }
+ painter.end();
+}
+
+/*
+ SearchLineEdit is an enhanced QLineEdit
+ - A Search icon on the left with optional menu
+ - When there is no text and doesn't have focus an "inactive text" is displayed
+ - When there is text a clear button is displayed on the right hand side
+ */
+SearchLineEdit::SearchLineEdit(QWidget *parent) : ExLineEdit(parent),
+ m_searchButton(new SearchButton(this))
+{
+ connect(lineEdit(), SIGNAL(textChanged(const QString &)),
+ this, SIGNAL(textChanged(const QString &)));
+ setLeftWidget(m_searchButton);
+ m_inactiveText = tr("Search");
+
+ QSizePolicy policy = sizePolicy();
+ setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy());
+}
+
+void SearchLineEdit::paintEvent(QPaintEvent *event)
+{
+ if (lineEdit()->text().isEmpty() && !hasFocus() && !m_inactiveText.isEmpty()) {
+ ExLineEdit::paintEvent(event);
+ QStyleOptionFrameV2 panel;
+ initStyleOption(&panel);
+ QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
+ QFontMetrics fm = fontMetrics();
+ int horizontalMargin = lineEdit()->x();
+ QRect lineRect(horizontalMargin + r.x(), r.y() + (r.height() - fm.height() + 1) / 2,
+ r.width() - 2 * horizontalMargin, fm.height());
+ QPainter painter(this);
+ painter.setPen(palette().brush(QPalette::Disabled, QPalette::Text).color());
+ painter.drawText(lineRect, Qt::AlignLeft|Qt::AlignVCenter, m_inactiveText);
+ } else {
+ ExLineEdit::paintEvent(event);
+ }
+}
+
+void SearchLineEdit::resizeEvent(QResizeEvent *event)
+{
+ updateGeometries();
+ ExLineEdit::resizeEvent(event);
+}
+
+void SearchLineEdit::updateGeometries()
+{
+ int menuHeight = height();
+ int menuWidth = menuHeight + 1;
+ if (!m_searchButton->m_menu)
+ menuWidth = (menuHeight / 5) * 4;
+ m_searchButton->resize(QSize(menuWidth, menuHeight));
+}
+
+QString SearchLineEdit::inactiveText() const
+{
+ return m_inactiveText;
+}
+
+void SearchLineEdit::setInactiveText(const QString &text)
+{
+ m_inactiveText = text;
+}
+
+void SearchLineEdit::setMenu(QMenu *menu)
+{
+ if (m_searchButton->m_menu)
+ m_searchButton->m_menu->deleteLater();
+ m_searchButton->m_menu = menu;
+ updateGeometries();
+}
+
+QMenu *SearchLineEdit::menu() const
+{
+ if (!m_searchButton->m_menu) {
+ m_searchButton->m_menu = new QMenu(m_searchButton);
+ if (isVisible())
+ (const_cast<SearchLineEdit*>(this))->updateGeometries();
+ }
+ return m_searchButton->m_menu;
+}
diff --git a/src/searchlineedit.h b/src/searchlineedit.h
new file mode 100644
index 00000000..2f2a8362
--- /dev/null
+++ b/src/searchlineedit.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef SEARCHLINEEDIT_H
+#define SEARCHLINEEDIT_H
+
+#include "urllineedit.h"
+
+#include <QtGui/QLineEdit>
+#include <QtGui/QAbstractButton>
+
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
+
+class SearchButton;
+
+/*
+ Clear button on the right hand side of the search widget.
+ Hidden by default
+ "A circle with an X in it"
+ */
+class ClearButton : public QAbstractButton
+{
+ Q_OBJECT
+
+public:
+ ClearButton(QWidget *parent = 0);
+ void paintEvent(QPaintEvent *event);
+
+public slots:
+ void textChanged(const QString &text);
+};
+
+
+class SearchLineEdit : public ExLineEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(QString inactiveText READ inactiveText WRITE setInactiveText)
+
+signals:
+ void textChanged(const QString &text);
+
+public:
+ SearchLineEdit(QWidget *parent = 0);
+
+ QString inactiveText() const;
+ void setInactiveText(const QString &text);
+
+ QMenu *menu() const;
+ void setMenu(QMenu *menu);
+
+protected:
+ void resizeEvent(QResizeEvent *event);
+ void paintEvent(QPaintEvent *event);
+
+private:
+ void updateGeometries();
+
+ SearchButton *m_searchButton;
+ QString m_inactiveText;
+};
+
+#endif // SEARCHLINEEDIT_H
+
diff --git a/src/settings.cpp b/src/settings.cpp
new file mode 100644
index 00000000..a3a85ee1
--- /dev/null
+++ b/src/settings.cpp
@@ -0,0 +1,320 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "settings.h"
+
+#include "browserapplication.h"
+#include "browsermainwindow.h"
+#include "cookiejar.h"
+#include "history.h"
+#include "networkaccessmanager.h"
+#include "webview.h"
+
+#include <QtCore/QSettings>
+#include <QtGui/QtGui>
+#include <QtWebKit/QtWebKit>
+
+SettingsDialog::SettingsDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ setupUi(this);
+ connect(exceptionsButton, SIGNAL(clicked()), this, SLOT(showExceptions()));
+ connect(setHomeToCurrentPageButton, SIGNAL(clicked()), this, SLOT(setHomeToCurrentPage()));
+ connect(cookiesButton, SIGNAL(clicked()), this, SLOT(showCookies()));
+ connect(standardFontButton, SIGNAL(clicked()), this, SLOT(chooseFont()));
+ connect(fixedFontButton, SIGNAL(clicked()), this, SLOT(chooseFixedFont()));
+
+ loadDefaults();
+ loadFromSettings();
+}
+
+void SettingsDialog::loadDefaults()
+{
+ QWebSettings *defaultSettings = QWebSettings::globalSettings();
+ QString standardFontFamily = defaultSettings->fontFamily(QWebSettings::StandardFont);
+ int standardFontSize = defaultSettings->fontSize(QWebSettings::DefaultFontSize);
+ standardFont = QFont(standardFontFamily, standardFontSize);
+ standardLabel->setText(QString(QLatin1String("%1 %2")).arg(standardFont.family()).arg(standardFont.pointSize()));
+
+ QString fixedFontFamily = defaultSettings->fontFamily(QWebSettings::FixedFont);
+ int fixedFontSize = defaultSettings->fontSize(QWebSettings::DefaultFixedFontSize);
+ fixedFont = QFont(fixedFontFamily, fixedFontSize);
+ fixedLabel->setText(QString(QLatin1String("%1 %2")).arg(fixedFont.family()).arg(fixedFont.pointSize()));
+
+ downloadsLocation->setText(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation));
+
+ enableJavascript->setChecked(defaultSettings->testAttribute(QWebSettings::JavascriptEnabled));
+ enablePlugins->setChecked(defaultSettings->testAttribute(QWebSettings::PluginsEnabled));
+}
+
+void SettingsDialog::loadFromSettings()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("MainWindow"));
+ QString defaultHome = QLatin1String("http://www.trolltech.com");
+ homeLineEdit->setText(settings.value(QLatin1String("home"), defaultHome).toString());
+ settings.endGroup();
+
+ settings.beginGroup(QLatin1String("history"));
+ int historyExpire = settings.value(QLatin1String("historyExpire")).toInt();
+ int idx = 0;
+ switch (historyExpire) {
+ case 1: idx = 0; break;
+ case 7: idx = 1; break;
+ case 14: idx = 2; break;
+ case 30: idx = 3; break;
+ case 365: idx = 4; break;
+ case -1: idx = 5; break;
+ default:
+ idx = 5;
+ }
+ expireHistory->setCurrentIndex(idx);
+ settings.endGroup();
+
+ settings.beginGroup(QLatin1String("downloadmanager"));
+ QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), downloadsLocation->text()).toString();
+ downloadsLocation->setText(downloadDirectory);
+ settings.endGroup();
+
+ settings.beginGroup(QLatin1String("general"));
+ openLinksIn->setCurrentIndex(settings.value(QLatin1String("openLinksIn"), openLinksIn->currentIndex()).toInt());
+
+ settings.endGroup();
+
+ // Appearance
+ settings.beginGroup(QLatin1String("websettings"));
+ fixedFont = qVariantValue<QFont>(settings.value(QLatin1String("fixedFont"), fixedFont));
+ standardFont = qVariantValue<QFont>(settings.value(QLatin1String("standardFont"), standardFont));
+
+ standardLabel->setText(QString(QLatin1String("%1 %2")).arg(standardFont.family()).arg(standardFont.pointSize()));
+ fixedLabel->setText(QString(QLatin1String("%1 %2")).arg(fixedFont.family()).arg(fixedFont.pointSize()));
+
+ enableJavascript->setChecked(settings.value(QLatin1String("enableJavascript"), enableJavascript->isChecked()).toBool());
+ enablePlugins->setChecked(settings.value(QLatin1String("enablePlugins"), enablePlugins->isChecked()).toBool());
+ userStyleSheet->setText(settings.value(QLatin1String("userStyleSheet")).toUrl().toString());
+ settings.endGroup();
+
+ // Privacy
+ settings.beginGroup(QLatin1String("cookies"));
+
+ CookieJar *jar = BrowserApplication::cookieJar();
+ QByteArray value = settings.value(QLatin1String("acceptCookies"), QLatin1String("AcceptOnlyFromSitesNavigatedTo")).toByteArray();
+ QMetaEnum acceptPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("AcceptPolicy"));
+ CookieJar::AcceptPolicy acceptCookies = acceptPolicyEnum.keyToValue(value) == -1 ?
+ CookieJar::AcceptOnlyFromSitesNavigatedTo :
+ static_cast<CookieJar::AcceptPolicy>(acceptPolicyEnum.keyToValue(value));
+ switch(acceptCookies) {
+ case CookieJar::AcceptAlways:
+ acceptCombo->setCurrentIndex(0);
+ break;
+ case CookieJar::AcceptNever:
+ acceptCombo->setCurrentIndex(1);
+ break;
+ case CookieJar::AcceptOnlyFromSitesNavigatedTo:
+ acceptCombo->setCurrentIndex(2);
+ break;
+ }
+
+ value = settings.value(QLatin1String("keepCookiesUntil"), QLatin1String("Expire")).toByteArray();
+ QMetaEnum keepPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("KeepPolicy"));
+ CookieJar::KeepPolicy keepCookies = keepPolicyEnum.keyToValue(value) == -1 ?
+ CookieJar::KeepUntilExpire :
+ static_cast<CookieJar::KeepPolicy>(keepPolicyEnum.keyToValue(value));
+ switch(keepCookies) {
+ case CookieJar::KeepUntilExpire:
+ keepUntilCombo->setCurrentIndex(0);
+ break;
+ case CookieJar::KeepUntilExit:
+ keepUntilCombo->setCurrentIndex(1);
+ break;
+ case CookieJar::KeepUntilTimeLimit:
+ keepUntilCombo->setCurrentIndex(2);
+ break;
+ }
+ settings.endGroup();
+
+
+ // Proxy
+ settings.beginGroup(QLatin1String("proxy"));
+ proxySupport->setChecked(settings.value(QLatin1String("enabled"), false).toBool());
+ proxyType->setCurrentIndex(settings.value(QLatin1String("type"), 0).toInt());
+ proxyHostName->setText(settings.value(QLatin1String("hostName")).toString());
+ proxyPort->setValue(settings.value(QLatin1String("port"), 1080).toInt());
+ proxyUserName->setText(settings.value(QLatin1String("userName")).toString());
+ proxyPassword->setText(settings.value(QLatin1String("password")).toString());
+ settings.endGroup();
+}
+
+void SettingsDialog::saveToSettings()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("MainWindow"));
+ settings.setValue(QLatin1String("home"), homeLineEdit->text());
+ settings.endGroup();
+
+ settings.beginGroup(QLatin1String("general"));
+ settings.setValue(QLatin1String("openLinksIn"), openLinksIn->currentIndex());
+ settings.endGroup();
+
+ settings.beginGroup(QLatin1String("history"));
+ int historyExpire = expireHistory->currentIndex();
+ int idx = -1;
+ switch (historyExpire) {
+ case 0: idx = 1; break;
+ case 1: idx = 7; break;
+ case 2: idx = 14; break;
+ case 3: idx = 30; break;
+ case 4: idx = 365; break;
+ case 5: idx = -1; break;
+ }
+ settings.setValue(QLatin1String("historyExpire"), idx);
+ settings.endGroup();
+
+ // Appearance
+ settings.beginGroup(QLatin1String("websettings"));
+ settings.setValue(QLatin1String("fixedFont"), fixedFont);
+ settings.setValue(QLatin1String("standardFont"), standardFont);
+ settings.setValue(QLatin1String("enableJavascript"), enableJavascript->isChecked());
+ settings.setValue(QLatin1String("enablePlugins"), enablePlugins->isChecked());
+ QString userStyleSheetString = userStyleSheet->text();
+ if (QFile::exists(userStyleSheetString))
+ settings.setValue(QLatin1String("userStyleSheet"), QUrl::fromLocalFile(userStyleSheetString));
+ else
+ settings.setValue(QLatin1String("userStyleSheet"), QUrl(userStyleSheetString));
+ settings.endGroup();
+
+ //Privacy
+ settings.beginGroup(QLatin1String("cookies"));
+
+ CookieJar::KeepPolicy keepCookies;
+ switch(acceptCombo->currentIndex()) {
+ default:
+ case 0:
+ keepCookies = CookieJar::KeepUntilExpire;
+ break;
+ case 1:
+ keepCookies = CookieJar::KeepUntilExit;
+ break;
+ case 2:
+ keepCookies = CookieJar::KeepUntilTimeLimit;
+ break;
+ }
+ CookieJar *jar = BrowserApplication::cookieJar();
+ QMetaEnum acceptPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("AcceptPolicy"));
+ settings.setValue(QLatin1String("acceptCookies"), QLatin1String(acceptPolicyEnum.valueToKey(keepCookies)));
+
+ CookieJar::KeepPolicy keepPolicy;
+ switch(keepUntilCombo->currentIndex()) {
+ default:
+ case 0:
+ keepPolicy = CookieJar::KeepUntilExpire;
+ break;
+ case 1:
+ keepPolicy = CookieJar::KeepUntilExit;
+ break;
+ case 2:
+ keepPolicy = CookieJar::KeepUntilTimeLimit;
+ break;
+ }
+
+ QMetaEnum keepPolicyEnum = jar->staticMetaObject.enumerator(jar->staticMetaObject.indexOfEnumerator("KeepPolicy"));
+ settings.setValue(QLatin1String("keepCookiesUntil"), QLatin1String(keepPolicyEnum.valueToKey(keepPolicy)));
+
+ settings.endGroup();
+
+ // proxy
+ settings.beginGroup(QLatin1String("proxy"));
+ settings.setValue(QLatin1String("enabled"), proxySupport->isChecked());
+ settings.setValue(QLatin1String("type"), proxyType->currentIndex());
+ settings.setValue(QLatin1String("hostName"), proxyHostName->text());
+ settings.setValue(QLatin1String("port"), proxyPort->text());
+ settings.setValue(QLatin1String("userName"), proxyUserName->text());
+ settings.setValue(QLatin1String("password"), proxyPassword->text());
+ settings.endGroup();
+
+ BrowserApplication::instance()->loadSettings();
+ BrowserApplication::networkAccessManager()->loadSettings();
+ BrowserApplication::cookieJar()->loadSettings();
+ BrowserApplication::historyManager()->loadSettings();
+}
+
+void SettingsDialog::accept()
+{
+ saveToSettings();
+ QDialog::accept();
+}
+
+void SettingsDialog::showCookies()
+{
+ CookiesDialog *dialog = new CookiesDialog(BrowserApplication::cookieJar(), this);
+ dialog->exec();
+}
+
+void SettingsDialog::showExceptions()
+{
+ CookiesExceptionsDialog *dialog = new CookiesExceptionsDialog(BrowserApplication::cookieJar(), this);
+ dialog->exec();
+}
+
+void SettingsDialog::chooseFont()
+{
+ bool ok;
+ QFont font = QFontDialog::getFont(&ok, standardFont, this);
+ if ( ok ) {
+ standardFont = font;
+ standardLabel->setText(QString(QLatin1String("%1 %2")).arg(font.family()).arg(font.pointSize()));
+ }
+}
+
+void SettingsDialog::chooseFixedFont()
+{
+ bool ok;
+ QFont font = QFontDialog::getFont(&ok, fixedFont, this);
+ if ( ok ) {
+ fixedFont = font;
+ fixedLabel->setText(QString(QLatin1String("%1 %2")).arg(font.family()).arg(font.pointSize()));
+ }
+}
+
+void SettingsDialog::setHomeToCurrentPage()
+{
+ BrowserMainWindow *mw = static_cast<BrowserMainWindow*>(parent());
+ WebView *webView = mw->currentTab();
+ if (webView)
+ homeLineEdit->setText(webView->url().toString());
+}
+
diff --git a/src/settings.h b/src/settings.h
new file mode 100644
index 00000000..dfd8dd15
--- /dev/null
+++ b/src/settings.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include <QtGui/QDialog>
+#include "ui_settings.h"
+
+class SettingsDialog : public QDialog, public Ui_Settings
+{
+ Q_OBJECT
+
+public:
+ SettingsDialog(QWidget *parent = 0);
+ void accept();
+
+private slots:
+ void loadDefaults();
+ void loadFromSettings();
+ void saveToSettings();
+
+ void setHomeToCurrentPage();
+ void showCookies();
+ void showExceptions();
+
+ void chooseFont();
+ void chooseFixedFont();
+
+private:
+ QFont standardFont;
+ QFont fixedFont;
+};
+
+#endif // SETTINGS_H
+
diff --git a/src/settings.ui b/src/settings.ui
new file mode 100644
index 00000000..3491ce0b
--- /dev/null
+++ b/src/settings.ui
@@ -0,0 +1,614 @@
+<ui version="4.0" >
+ <class>Settings</class>
+ <widget class="QDialog" name="Settings" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>657</width>
+ <height>322</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Settings</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" >
+ <item row="2" column="0" >
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QTabWidget" name="tabWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>627</width>
+ <height>243</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>General</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Home:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <widget class="QLineEdit" name="homeLineEdit" />
+ </item>
+ <item row="1" column="1" >
+ <widget class="QPushButton" name="setHomeToCurrentPageButton" >
+ <property name="text" >
+ <string>Set to current page</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <spacer name="horizontalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>280</width>
+ <height>18</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Remove history items:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="2" >
+ <widget class="QComboBox" name="expireHistory" >
+ <item>
+ <property name="text" >
+ <string>After one day</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>After one week</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>After two weeks</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>After one month</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>After one year</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Manually</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_7" >
+ <property name="text" >
+ <string>Save downloads to:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2" >
+ <widget class="QLineEdit" name="downloadsLocation" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_8" >
+ <property name="text" >
+ <string>Open links from applications:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="2" >
+ <widget class="QComboBox" name="openLinksIn" >
+ <item>
+ <property name="text" >
+ <string>In a tab in the current window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>In a new window</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>391</width>
+ <height>262</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>627</width>
+ <height>243</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>Appearance</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>Standard font:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="standardLabel" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape" >
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="text" >
+ <string>Times 16</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QPushButton" name="standardFontButton" >
+ <property name="text" >
+ <string>Select...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_6" >
+ <property name="text" >
+ <string>Fixed-width font:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="fixedLabel" >
+ <property name="frameShape" >
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="text" >
+ <string>Courier 13</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QPushButton" name="fixedFontButton" >
+ <property name="text" >
+ <string>Select...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>93</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>627</width>
+ <height>243</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>Privacy</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3" >
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Web Content</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2" >
+ <item>
+ <widget class="QCheckBox" name="enablePlugins" >
+ <property name="text" >
+ <string>Enable Plugins</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enableJavascript" >
+ <property name="text" >
+ <string>Enable Javascript</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="cookiesGroupBox" >
+ <property name="title" >
+ <string>Cookies</string>
+ </property>
+ <layout class="QGridLayout" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Accept Cookies:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QComboBox" name="acceptCombo" >
+ <item>
+ <property name="text" >
+ <string>Always</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Never</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Only from sites you navigate to</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QPushButton" name="exceptionsButton" >
+ <property name="text" >
+ <string>Exceptions...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Keep until:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QComboBox" name="keepUntilCombo" >
+ <item>
+ <property name="text" >
+ <string>They expire</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>I exit the application</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>At most 90 days</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QPushButton" name="cookiesButton" >
+ <property name="text" >
+ <string>Cookies...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>371</width>
+ <height>177</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>627</width>
+ <height>243</height>
+ </rect>
+ </property>
+ <attribute name="title" >
+ <string>Proxy</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout" >
+ <item>
+ <widget class="QGroupBox" name="proxySupport" >
+ <property name="title" >
+ <string>Enable proxy</string>
+ </property>
+ <property name="checkable" >
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_6" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_9" >
+ <property name="text" >
+ <string>Type:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <widget class="QComboBox" name="proxyType" >
+ <item>
+ <property name="text" >
+ <string>Socks5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Http</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_10" >
+ <property name="text" >
+ <string>Host:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="2" >
+ <widget class="QLineEdit" name="proxyHostName" />
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_11" >
+ <property name="text" >
+ <string>Port:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QSpinBox" name="proxyPort" >
+ <property name="maximum" >
+ <number>10000</number>
+ </property>
+ <property name="value" >
+ <number>1080</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <spacer name="horizontalSpacer_2" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>293</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_12" >
+ <property name="text" >
+ <string>User Name:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2" >
+ <widget class="QLineEdit" name="proxyUserName" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_13" >
+ <property name="text" >
+ <string>Password:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="2" >
+ <widget class="QLineEdit" name="proxyPassword" >
+ <property name="echoMode" >
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <spacer name="verticalSpacer_2" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_5" >
+ <attribute name="title" >
+ <string>Advanced</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2" >
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_14" >
+ <property name="text" >
+ <string>Style Sheet:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLineEdit" name="userStyleSheet" />
+ </item>
+ <item row="1" column="1" >
+ <spacer name="verticalSpacer_3" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>176</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>Settings</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>Settings</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/squeezelabel.cpp b/src/squeezelabel.cpp
new file mode 100644
index 00000000..f8687ca7
--- /dev/null
+++ b/src/squeezelabel.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "squeezelabel.h"
+
+SqueezeLabel::SqueezeLabel(QWidget *parent) : QLabel(parent)
+{
+}
+
+void SqueezeLabel::paintEvent(QPaintEvent *event)
+{
+ QFontMetrics fm = fontMetrics();
+ if (fm.width(text()) > contentsRect().width()) {
+ QString elided = fm.elidedText(text(), Qt::ElideMiddle, width());
+ QString oldText = text();
+ setText(elided);
+ QLabel::paintEvent(event);
+ setText(oldText);
+ } else {
+ QLabel::paintEvent(event);
+ }
+}
diff --git a/src/squeezelabel.h b/src/squeezelabel.h
new file mode 100644
index 00000000..8dd996ef
--- /dev/null
+++ b/src/squeezelabel.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef SQUEEZELABEL_H
+#define SQUEEZELABEL_H
+
+#include <QtGui/QLabel>
+
+class SqueezeLabel : public QLabel
+{
+ Q_OBJECT
+
+public:
+ SqueezeLabel(QWidget *parent = 0);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+
+};
+
+#endif // SQUEEZELABEL_H
+
diff --git a/src/tabwidget.cpp b/src/tabwidget.cpp
new file mode 100644
index 00000000..f873ec6f
--- /dev/null
+++ b/src/tabwidget.cpp
@@ -0,0 +1,859 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "tabwidget.h"
+
+#include "browserapplication.h"
+#include "browsermainwindow.h"
+#include "history.h"
+#include "urllineedit.h"
+#include "webview.h"
+
+#include <QtGui/QClipboard>
+#include <QtGui/QCompleter>
+#include <QtGui/QListView>
+#include <QtGui/QMenu>
+#include <QtGui/QMessageBox>
+#include <QtGui/QMouseEvent>
+#include <QtGui/QStackedWidget>
+#include <QtGui/QStyle>
+#include <QtGui/QToolButton>
+
+#include <QtCore/QDebug>
+
+TabBar::TabBar(QWidget *parent)
+ : QTabBar(parent)
+{
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ setAcceptDrops(true);
+ connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(contextMenuRequested(const QPoint &)));
+
+ QString alt = QLatin1String("Alt+%1");
+ for (int i = 1; i <= 10; ++i) {
+ int key = i;
+ if (key == 10)
+ key = 0;
+ QShortcut *shortCut = new QShortcut(alt.arg(key), this);
+ m_tabShortcuts.append(shortCut);
+ connect(shortCut, SIGNAL(activated()), this, SLOT(selectTabAction()));
+ }
+}
+
+void TabBar::selectTabAction()
+{
+ if (QShortcut *shortCut = qobject_cast<QShortcut*>(sender())) {
+ int index = m_tabShortcuts.indexOf(shortCut);
+ if (index == 0)
+ index = 10;
+ setCurrentIndex(index);
+ }
+}
+
+void TabBar::contextMenuRequested(const QPoint &position)
+{
+ QMenu menu;
+ menu.addAction(tr("New &Tab"), this, SIGNAL(newTab()), QKeySequence::AddTab);
+ int index = tabAt(position);
+ if (-1 != index) {
+ QAction *action = menu.addAction(tr("Clone Tab"),
+ this, SLOT(cloneTab()));
+ action->setData(index);
+
+ menu.addSeparator();
+
+ action = menu.addAction(tr("&Close Tab"),
+ this, SLOT(closeTab()), QKeySequence::Close);
+ action->setData(index);
+
+ action = menu.addAction(tr("Close &Other Tabs"),
+ this, SLOT(closeOtherTabs()));
+ action->setData(index);
+
+ menu.addSeparator();
+
+ action = menu.addAction(tr("Reload Tab"),
+ this, SLOT(reloadTab()), QKeySequence::Refresh);
+ action->setData(index);
+ } else {
+ menu.addSeparator();
+ }
+ menu.addAction(tr("Reload All Tabs"), this, SIGNAL(reloadAllTabs()));
+ menu.exec(QCursor::pos());
+}
+
+void TabBar::cloneTab()
+{
+ if (QAction *action = qobject_cast<QAction*>(sender())) {
+ int index = action->data().toInt();
+ emit cloneTab(index);
+ }
+}
+
+void TabBar::closeTab()
+{
+ if (QAction *action = qobject_cast<QAction*>(sender())) {
+ int index = action->data().toInt();
+ emit closeTab(index);
+ }
+}
+
+void TabBar::closeOtherTabs()
+{
+ if (QAction *action = qobject_cast<QAction*>(sender())) {
+ int index = action->data().toInt();
+ emit closeOtherTabs(index);
+ }
+}
+
+void TabBar::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ m_dragStartPos = event->pos();
+ QTabBar::mousePressEvent(event);
+}
+
+void TabBar::mouseMoveEvent(QMouseEvent *event)
+{
+ if (event->buttons() == Qt::LeftButton
+ && (event->pos() - m_dragStartPos).manhattanLength() > QApplication::startDragDistance()) {
+ QDrag *drag = new QDrag(this);
+ QMimeData *mimeData = new QMimeData;
+ QList<QUrl> urls;
+ int index = tabAt(event->pos());
+ QUrl url = tabData(index).toUrl();
+ urls.append(url);
+ mimeData->setUrls(urls);
+ mimeData->setText(tabText(index));
+ mimeData->setData(QLatin1String("action"), "tab-reordering");
+ drag->setMimeData(mimeData);
+ drag->exec();
+ }
+ QTabBar::mouseMoveEvent(event);
+}
+
+void TabBar::dragEnterEvent(QDragEnterEvent *event)
+{
+ const QMimeData *mimeData = event->mimeData();
+ QStringList formats = mimeData->formats();
+ if (formats.contains(QLatin1String("action"))
+ && (mimeData->data(QLatin1String("action")) == "tab-reordering")) {
+ event->acceptProposedAction();
+ }
+ QTabBar::dragEnterEvent(event);
+}
+
+void TabBar::dropEvent(QDropEvent *event)
+{
+ int fromIndex = tabAt(m_dragStartPos);
+ int toIndex = tabAt(event->pos());
+ if (fromIndex != toIndex) {
+ emit tabMoveRequested(fromIndex, toIndex);
+ event->acceptProposedAction();
+ }
+ QTabBar::dropEvent(event);
+}
+
+// When index is -1 index chooses the current tab
+void TabWidget::reloadTab(int index)
+{
+ if (index < 0)
+ index = currentIndex();
+ if (index < 0 || index >= count())
+ return;
+
+ QWidget *widget = this->widget(index);
+ if (WebView *tab = qobject_cast<WebView*>(widget))
+ tab->reload();
+}
+
+void TabBar::reloadTab()
+{
+ if (QAction *action = qobject_cast<QAction*>(sender())) {
+ int index = action->data().toInt();
+ emit reloadTab(index);
+ }
+}
+
+TabWidget::TabWidget(QWidget *parent)
+ : QTabWidget(parent)
+ , m_recentlyClosedTabsAction(0)
+ , m_newTabAction(0)
+ , m_closeTabAction(0)
+ , m_nextTabAction(0)
+ , m_previousTabAction(0)
+ , m_recentlyClosedTabsMenu(0)
+ , m_lineEditCompleter(0)
+ , m_lineEdits(0)
+ , m_tabBar(new TabBar(this))
+{
+ setElideMode(Qt::ElideRight);
+
+ connect(m_tabBar, SIGNAL(newTab()), this, SLOT(newTab()));
+ connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(closeTab(int)));
+ connect(m_tabBar, SIGNAL(cloneTab(int)), this, SLOT(cloneTab(int)));
+ connect(m_tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(closeOtherTabs(int)));
+ connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(reloadTab(int)));
+ connect(m_tabBar, SIGNAL(reloadAllTabs()), this, SLOT(reloadAllTabs()));
+ connect(m_tabBar, SIGNAL(tabMoveRequested(int, int)), this, SLOT(moveTab(int, int)));
+ setTabBar(m_tabBar);
+
+ // Actions
+ m_newTabAction = new QAction(QIcon(QLatin1String(":addtab.png")), tr("New &Tab"), this);
+ m_newTabAction->setShortcuts(QKeySequence::AddTab);
+ m_newTabAction->setIconVisibleInMenu(false);
+ connect(m_newTabAction, SIGNAL(triggered()), this, SLOT(newTab()));
+
+ m_closeTabAction = new QAction(QIcon(QLatin1String(":closetab.png")), tr("&Close Tab"), this);
+ m_closeTabAction->setShortcuts(QKeySequence::Close);
+ m_closeTabAction->setIconVisibleInMenu(false);
+ connect(m_closeTabAction, SIGNAL(triggered()), this, SLOT(closeTab()));
+
+ m_nextTabAction = new QAction(tr("Show Next Tab"), this);
+ QList<QKeySequence> shortcuts;
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BraceRight));
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_PageDown));
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BracketRight));
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Less));
+ m_nextTabAction->setShortcuts(shortcuts);
+ connect(m_nextTabAction, SIGNAL(triggered()), this, SLOT(nextTab()));
+
+ m_previousTabAction = new QAction(tr("Show Previous Tab"), this);
+ shortcuts.clear();
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BraceLeft));
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_PageUp));
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_BracketLeft));
+ shortcuts.append(QKeySequence(Qt::CTRL | Qt::Key_Greater));
+ m_previousTabAction->setShortcuts(shortcuts);
+ connect(m_previousTabAction, SIGNAL(triggered()), this, SLOT(previousTab()));
+
+ m_recentlyClosedTabsMenu = new QMenu(this);
+ connect(m_recentlyClosedTabsMenu, SIGNAL(aboutToShow()),
+ this, SLOT(aboutToShowRecentTabsMenu()));
+ connect(m_recentlyClosedTabsMenu, SIGNAL(triggered(QAction *)),
+ this, SLOT(aboutToShowRecentTriggeredAction(QAction *)));
+ m_recentlyClosedTabsAction = new QAction(tr("Recently Closed Tabs"), this);
+ m_recentlyClosedTabsAction->setMenu(m_recentlyClosedTabsMenu);
+ m_recentlyClosedTabsAction->setEnabled(false);
+
+ // corner buttons
+ QToolButton *addTabButton = new QToolButton(this);
+ addTabButton->setDefaultAction(m_newTabAction);
+ addTabButton->setAutoRaise(true);
+ addTabButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ setCornerWidget(addTabButton, Qt::TopLeftCorner);
+
+ QToolButton *closeTabButton = new QToolButton(this);
+ closeTabButton->setDefaultAction(m_closeTabAction);
+ closeTabButton->setAutoRaise(true);
+ closeTabButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ setCornerWidget(closeTabButton, Qt::TopRightCorner);
+
+ connect(this, SIGNAL(currentChanged(int)),
+ this, SLOT(currentChanged(int)));
+
+ m_lineEdits = new QStackedWidget(this);
+}
+
+void TabWidget::clear()
+{
+ // clear the recently closed tabs
+ m_recentlyClosedTabs.clear();
+ // clear the line edit history
+ for (int i = 0; i < m_lineEdits->count(); ++i) {
+ QLineEdit *qLineEdit = lineEdit(i);
+ qLineEdit->setText(qLineEdit->text());
+ }
+}
+
+void TabWidget::moveTab(int fromIndex, int toIndex)
+{
+ disconnect(this, SIGNAL(currentChanged(int)),
+ this, SLOT(currentChanged(int)));
+
+ QWidget *tabWidget = widget(fromIndex);
+ QIcon icon = tabIcon(fromIndex);
+ QString text = tabText(fromIndex);
+ QVariant data = m_tabBar->tabData(fromIndex);
+ removeTab(fromIndex);
+ insertTab(toIndex, tabWidget, icon, text);
+ m_tabBar->setTabData(toIndex, data);
+ connect(this, SIGNAL(currentChanged(int)),
+ this, SLOT(currentChanged(int)));
+ setCurrentIndex(toIndex);
+}
+
+void TabWidget::addWebAction(QAction *action, QWebPage::WebAction webAction)
+{
+ if (!action)
+ return;
+ m_actions.append(new WebActionMapper(action, webAction, this));
+}
+
+void TabWidget::currentChanged(int index)
+{
+ WebView *webView = this->webView(index);
+ if (!webView)
+ return;
+
+ Q_ASSERT(m_lineEdits->count() == count());
+
+ WebView *oldWebView = this->webView(m_lineEdits->currentIndex());
+ if (oldWebView) {
+ disconnect(oldWebView, SIGNAL(statusBarMessage(const QString&)),
+ this, SIGNAL(showStatusBarMessage(const QString&)));
+ disconnect(oldWebView->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)),
+ this, SIGNAL(linkHovered(const QString&)));
+ disconnect(oldWebView, SIGNAL(loadProgress(int)),
+ this, SIGNAL(loadProgress(int)));
+ }
+
+ connect(webView, SIGNAL(statusBarMessage(const QString&)),
+ this, SIGNAL(showStatusBarMessage(const QString&)));
+ connect(webView->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString&)),
+ this, SIGNAL(linkHovered(const QString&)));
+ connect(webView, SIGNAL(loadProgress(int)),
+ this, SIGNAL(loadProgress(int)));
+
+ for (int i = 0; i < m_actions.count(); ++i) {
+ WebActionMapper *mapper = m_actions[i];
+ mapper->updateCurrent(webView->page());
+ }
+ emit setCurrentTitle(webView->title());
+ m_lineEdits->setCurrentIndex(index);
+ emit loadProgress(webView->progress());
+ emit showStatusBarMessage(webView->lastStatusBarText());
+ if (webView->url().isEmpty())
+ m_lineEdits->currentWidget()->setFocus();
+ else
+ webView->setFocus();
+}
+
+QAction *TabWidget::newTabAction() const
+{
+ return m_newTabAction;
+}
+
+QAction *TabWidget::closeTabAction() const
+{
+ return m_closeTabAction;
+}
+
+QAction *TabWidget::recentlyClosedTabsAction() const
+{
+ return m_recentlyClosedTabsAction;
+}
+
+QAction *TabWidget::nextTabAction() const
+{
+ return m_nextTabAction;
+}
+
+QAction *TabWidget::previousTabAction() const
+{
+ return m_previousTabAction;
+}
+
+QWidget *TabWidget::lineEditStack() const
+{
+ return m_lineEdits;
+}
+
+QLineEdit *TabWidget::currentLineEdit() const
+{
+ return lineEdit(m_lineEdits->currentIndex());
+}
+
+WebView *TabWidget::currentWebView() const
+{
+ return webView(currentIndex());
+}
+
+QLineEdit *TabWidget::lineEdit(int index) const
+{
+ UrlLineEdit *urlLineEdit = qobject_cast<UrlLineEdit*>(m_lineEdits->widget(index));
+ if (urlLineEdit)
+ return urlLineEdit->lineEdit();
+ return 0;
+}
+
+WebView *TabWidget::webView(int index) const
+{
+ QWidget *widget = this->widget(index);
+ if (WebView *webView = qobject_cast<WebView*>(widget)) {
+ return webView;
+ } else {
+ // optimization to delay creating the first webview
+ if (count() == 1) {
+ TabWidget *that = const_cast<TabWidget*>(this);
+ that->setUpdatesEnabled(false);
+ that->newTab();
+ that->closeTab(0);
+ that->setUpdatesEnabled(true);
+ return currentWebView();
+ }
+ }
+ return 0;
+}
+
+int TabWidget::webViewIndex(WebView *webView) const
+{
+ int index = indexOf(webView);
+ return index;
+}
+
+WebView *TabWidget::newTab(bool makeCurrent)
+{
+ // line edit
+ UrlLineEdit *urlLineEdit = new UrlLineEdit;
+ QLineEdit *lineEdit = urlLineEdit->lineEdit();
+ if (!m_lineEditCompleter && count() > 0) {
+ HistoryCompletionModel *completionModel = new HistoryCompletionModel(this);
+ completionModel->setSourceModel(BrowserApplication::historyManager()->historyFilterModel());
+ m_lineEditCompleter = new QCompleter(completionModel, this);
+ // Should this be in Qt by default?
+ QAbstractItemView *popup = m_lineEditCompleter->popup();
+ QListView *listView = qobject_cast<QListView*>(popup);
+ if (listView)
+ listView->setUniformItemSizes(true);
+ }
+ lineEdit->setCompleter(m_lineEditCompleter);
+ connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(lineEditReturnPressed()));
+ m_lineEdits->addWidget(urlLineEdit);
+ m_lineEdits->setSizePolicy(lineEdit->sizePolicy());
+
+ // optimization to delay creating the more expensive WebView, history, etc
+ if (count() == 0) {
+ QWidget *emptyWidget = new QWidget;
+ QPalette p = emptyWidget->palette();
+ p.setColor(QPalette::Window, palette().color(QPalette::Base));
+ emptyWidget->setPalette(p);
+ emptyWidget->setAutoFillBackground(true);
+ disconnect(this, SIGNAL(currentChanged(int)),
+ this, SLOT(currentChanged(int)));
+ addTab(emptyWidget, tr("(Untitled)"));
+ connect(this, SIGNAL(currentChanged(int)),
+ this, SLOT(currentChanged(int)));
+ return 0;
+ }
+
+ // webview
+ WebView *webView = new WebView;
+ urlLineEdit->setWebView(webView);
+ connect(webView, SIGNAL(loadStarted()),
+ this, SLOT(webViewLoadStarted()));
+ connect(webView, SIGNAL(loadFinished(bool)),
+ this, SLOT(webViewIconChanged()));
+ connect(webView, SIGNAL(iconChanged()),
+ this, SLOT(webViewIconChanged()));
+ connect(webView, SIGNAL(titleChanged(const QString &)),
+ this, SLOT(webViewTitleChanged(const QString &)));
+ connect(webView, SIGNAL(urlChanged(const QUrl &)),
+ this, SLOT(webViewUrlChanged(const QUrl &)));
+ connect(webView->page(), SIGNAL(windowCloseRequested()),
+ this, SLOT(windowCloseRequested()));
+ connect(webView->page(), SIGNAL(geometryChangeRequested(const QRect &)),
+ this, SIGNAL(geometryChangeRequested(const QRect &)));
+ connect(webView->page(), SIGNAL(printRequested(QWebFrame *)),
+ this, SIGNAL(printRequested(QWebFrame *)));
+ connect(webView->page(), SIGNAL(menuBarVisibilityChangeRequested(bool)),
+ this, SIGNAL(menuBarVisibilityChangeRequested(bool)));
+ connect(webView->page(), SIGNAL(statusBarVisibilityChangeRequested(bool)),
+ this, SIGNAL(statusBarVisibilityChangeRequested(bool)));
+ connect(webView->page(), SIGNAL(toolBarVisibilityChangeRequested(bool)),
+ this, SIGNAL(toolBarVisibilityChangeRequested(bool)));
+ addTab(webView, tr("(Untitled)"));
+ if (makeCurrent)
+ setCurrentWidget(webView);
+
+ // webview actions
+ for (int i = 0; i < m_actions.count(); ++i) {
+ WebActionMapper *mapper = m_actions[i];
+ mapper->addChild(webView->page()->action(mapper->webAction()));
+ }
+
+ if (count() == 1)
+ currentChanged(currentIndex());
+ emit tabsChanged();
+ return webView;
+}
+
+void TabWidget::reloadAllTabs()
+{
+ for (int i = 0; i < count(); ++i) {
+ QWidget *tabWidget = widget(i);
+ if (WebView *tab = qobject_cast<WebView*>(tabWidget)) {
+ tab->reload();
+ }
+ }
+}
+
+void TabWidget::lineEditReturnPressed()
+{
+ if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender())) {
+ emit loadPage(lineEdit->text());
+ if (m_lineEdits->currentWidget() == lineEdit)
+ currentWebView()->setFocus();
+ }
+}
+
+void TabWidget::windowCloseRequested()
+{
+ WebPage *webPage = qobject_cast<WebPage*>(sender());
+ WebView *webView = qobject_cast<WebView*>(webPage->view());
+ int index = webViewIndex(webView);
+ if (index >= 0) {
+ if (count() == 1)
+ webView->webPage()->mainWindow()->close();
+ else
+ closeTab(index);
+ }
+}
+
+void TabWidget::closeOtherTabs(int index)
+{
+ if (-1 == index)
+ return;
+ for (int i = count() - 1; i > index; --i)
+ closeTab(i);
+ for (int i = index - 1; i >= 0; --i)
+ closeTab(i);
+}
+
+// When index is -1 index chooses the current tab
+void TabWidget::cloneTab(int index)
+{
+ if (index < 0)
+ index = currentIndex();
+ if (index < 0 || index >= count())
+ return;
+ WebView *tab = newTab(false);
+ tab->setUrl(webView(index)->url());
+}
+
+// When index is -1 index chooses the current tab
+void TabWidget::closeTab(int index)
+{
+ if (index < 0)
+ index = currentIndex();
+ if (index < 0 || index >= count())
+ return;
+
+ bool hasFocus = false;
+ if (WebView *tab = webView(index)) {
+ if (tab->isModified()) {
+ QMessageBox closeConfirmation(tab);
+ closeConfirmation.setWindowFlags(Qt::Sheet);
+ closeConfirmation.setWindowTitle(tr("Do you really want to close this page?"));
+ closeConfirmation.setInformativeText(tr("You have modified this page and when closing it you would lose the modification.\n"
+ "Do you really want to close this page?\n"));
+ closeConfirmation.setIcon(QMessageBox::Question);
+ closeConfirmation.addButton(QMessageBox::Yes);
+ closeConfirmation.addButton(QMessageBox::No);
+ closeConfirmation.setEscapeButton(QMessageBox::No);
+ if (closeConfirmation.exec() == QMessageBox::No)
+ return;
+ }
+ hasFocus = tab->hasFocus();
+
+ m_recentlyClosedTabsAction->setEnabled(true);
+ m_recentlyClosedTabs.prepend(tab->url());
+ if (m_recentlyClosedTabs.size() >= TabWidget::m_recentlyClosedTabsSize)
+ m_recentlyClosedTabs.removeLast();
+ }
+ QWidget *lineEdit = m_lineEdits->widget(index);
+ m_lineEdits->removeWidget(lineEdit);
+ lineEdit->deleteLater();
+ QWidget *webView = widget(index);
+ removeTab(index);
+ webView->deleteLater();
+ emit tabsChanged();
+ if (hasFocus && count() > 0)
+ currentWebView()->setFocus();
+ if (count() == 0)
+ emit lastTabClosed();
+}
+
+void TabWidget::webViewLoadStarted()
+{
+ WebView *webView = qobject_cast<WebView*>(sender());
+ int index = webViewIndex(webView);
+ if (-1 != index) {
+ QIcon icon(QLatin1String(":loading.gif"));
+ setTabIcon(index, icon);
+ }
+}
+
+void TabWidget::webViewIconChanged()
+{
+ WebView *webView = qobject_cast<WebView*>(sender());
+ int index = webViewIndex(webView);
+ if (-1 != index) {
+ QIcon icon = BrowserApplication::instance()->icon(webView->url());
+ setTabIcon(index, icon);
+ }
+}
+
+void TabWidget::webViewTitleChanged(const QString &title)
+{
+ WebView *webView = qobject_cast<WebView*>(sender());
+ int index = webViewIndex(webView);
+ if (-1 != index) {
+ setTabText(index, title);
+ }
+ if (currentIndex() == index)
+ emit setCurrentTitle(title);
+ BrowserApplication::historyManager()->updateHistoryItem(webView->url(), title);
+}
+
+void TabWidget::webViewUrlChanged(const QUrl &url)
+{
+ WebView *webView = qobject_cast<WebView*>(sender());
+ int index = webViewIndex(webView);
+ if (-1 != index) {
+ m_tabBar->setTabData(index, url);
+ }
+ emit tabsChanged();
+}
+
+void TabWidget::aboutToShowRecentTabsMenu()
+{
+ m_recentlyClosedTabsMenu->clear();
+ for (int i = 0; i < m_recentlyClosedTabs.count(); ++i) {
+ QAction *action = new QAction(m_recentlyClosedTabsMenu);
+ action->setData(m_recentlyClosedTabs.at(i));
+ QIcon icon = BrowserApplication::instance()->icon(m_recentlyClosedTabs.at(i));
+ action->setIcon(icon);
+ action->setText(m_recentlyClosedTabs.at(i).toString());
+ m_recentlyClosedTabsMenu->addAction(action);
+ }
+}
+
+void TabWidget::aboutToShowRecentTriggeredAction(QAction *action)
+{
+ QUrl url = action->data().toUrl();
+ loadUrlInCurrentTab(url);
+}
+
+void TabWidget::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ if (!childAt(event->pos())
+ // Remove the line below when QTabWidget does not have a one pixel frame
+ && event->pos().y() < (tabBar()->y() + tabBar()->height())) {
+ newTab();
+ return;
+ }
+ QTabWidget::mouseDoubleClickEvent(event);
+}
+
+void TabWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ if (!childAt(event->pos())) {
+ m_tabBar->contextMenuRequested(event->pos());
+ return;
+ }
+ QTabWidget::contextMenuEvent(event);
+}
+
+void TabWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::MidButton && !childAt(event->pos())
+ // Remove the line below when QTabWidget does not have a one pixel frame
+ && event->pos().y() < (tabBar()->y() + tabBar()->height())) {
+ QUrl url(QApplication::clipboard()->text(QClipboard::Selection));
+ if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) {
+ WebView *webView = newTab();
+ webView->setUrl(url);
+ }
+ }
+}
+
+void TabWidget::loadUrlInCurrentTab(const QUrl &url)
+{
+ WebView *webView = currentWebView();
+ if (webView) {
+ webView->loadUrl(url);
+ webView->setFocus();
+ }
+}
+
+void TabWidget::nextTab()
+{
+ int next = currentIndex() + 1;
+ if (next == count())
+ next = 0;
+ setCurrentIndex(next);
+}
+
+void TabWidget::previousTab()
+{
+ int next = currentIndex() - 1;
+ if (next < 0)
+ next = count() - 1;
+ setCurrentIndex(next);
+}
+
+static const qint32 TabWidgetMagic = 0xaa;
+
+QByteArray TabWidget::saveState() const
+{
+ int version = 1;
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+
+ stream << qint32(TabWidgetMagic);
+ stream << qint32(version);
+
+ QStringList tabs;
+ for (int i = 0; i < count(); ++i) {
+ if (WebView *tab = qobject_cast<WebView*>(widget(i))) {
+ tabs.append(tab->url().toString());
+ } else {
+ tabs.append(QString::null);
+ }
+ }
+ stream << tabs;
+ stream << currentIndex();
+ return data;
+}
+
+bool TabWidget::restoreState(const QByteArray &state)
+{
+ int version = 1;
+ QByteArray sd = state;
+ QDataStream stream(&sd, QIODevice::ReadOnly);
+ if (stream.atEnd())
+ return false;
+
+ qint32 marker;
+ qint32 v;
+ stream >> marker;
+ stream >> v;
+ if (marker != TabWidgetMagic || v != version)
+ return false;
+
+ QStringList openTabs;
+ stream >> openTabs;
+
+ for (int i = 0; i < openTabs.count(); ++i) {
+ if (i != 0)
+ newTab();
+ loadPage(openTabs.at(i));
+ }
+
+ int currentTab;
+ stream >> currentTab;
+ setCurrentIndex(currentTab);
+
+ return true;
+}
+
+WebActionMapper::WebActionMapper(QAction *root, QWebPage::WebAction webAction, QObject *parent)
+ : QObject(parent)
+ , m_currentParent(0)
+ , m_root(root)
+ , m_webAction(webAction)
+{
+ if (!m_root)
+ return;
+ connect(m_root, SIGNAL(triggered()), this, SLOT(rootTriggered()));
+ connect(root, SIGNAL(destroyed(QObject *)), this, SLOT(rootDestroyed()));
+ root->setEnabled(false);
+}
+
+void WebActionMapper::rootDestroyed()
+{
+ m_root = 0;
+}
+
+void WebActionMapper::currentDestroyed()
+{
+ updateCurrent(0);
+}
+
+void WebActionMapper::addChild(QAction *action)
+{
+ if (!action)
+ return;
+ connect(action, SIGNAL(changed()), this, SLOT(childChanged()));
+}
+
+QWebPage::WebAction WebActionMapper::webAction() const
+{
+ return m_webAction;
+}
+
+void WebActionMapper::rootTriggered()
+{
+ if (m_currentParent) {
+ QAction *gotoAction = m_currentParent->action(m_webAction);
+ gotoAction->trigger();
+ }
+}
+
+void WebActionMapper::childChanged()
+{
+ if (QAction *source = qobject_cast<QAction*>(sender())) {
+ if (m_root
+ && m_currentParent
+ && source->parent() == m_currentParent) {
+ m_root->setChecked(source->isChecked());
+ m_root->setEnabled(source->isEnabled());
+ }
+ }
+}
+
+void WebActionMapper::updateCurrent(QWebPage *currentParent)
+{
+ if (m_currentParent)
+ disconnect(m_currentParent, SIGNAL(destroyed(QObject *)),
+ this, SLOT(currentDestroyed()));
+
+ m_currentParent = currentParent;
+ if (!m_root)
+ return;
+ if (!m_currentParent) {
+ m_root->setEnabled(false);
+ m_root->setChecked(false);
+ return;
+ }
+ QAction *source = m_currentParent->action(m_webAction);
+ m_root->setChecked(source->isChecked());
+ m_root->setEnabled(source->isEnabled());
+ connect(m_currentParent, SIGNAL(destroyed(QObject *)),
+ this, SLOT(currentDestroyed()));
+}
diff --git a/src/tabwidget.h b/src/tabwidget.h
new file mode 100644
index 00000000..1bdd56bf
--- /dev/null
+++ b/src/tabwidget.h
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef TABWIDGET_H
+#define TABWIDGET_H
+
+#include <QtGui/QTabBar>
+
+#include <QtGui/QShortcut>
+/*
+ Tab bar with a few more features such as a context menu and shortcuts
+ */
+class TabBar : public QTabBar
+{
+ Q_OBJECT
+
+signals:
+ void newTab();
+ void cloneTab(int index);
+ void closeTab(int index);
+ void closeOtherTabs(int index);
+ void reloadTab(int index);
+ void reloadAllTabs();
+ void tabMoveRequested(int fromIndex, int toIndex);
+
+public:
+ TabBar(QWidget *parent = 0);
+
+protected:
+ void mousePressEvent(QMouseEvent* event);
+ void mouseMoveEvent(QMouseEvent* event);
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dropEvent(QDropEvent *event);
+
+private slots:
+ void selectTabAction();
+ void cloneTab();
+ void closeTab();
+ void closeOtherTabs();
+ void reloadTab();
+ void contextMenuRequested(const QPoint &position);
+
+private:
+ QList<QShortcut*> m_tabShortcuts;
+ friend class TabWidget;
+
+ QPoint m_dragStartPos;
+ int m_dragCurrentIndex;
+};
+
+#include <QtWebKit/QWebPage>
+
+QT_BEGIN_NAMESPACE
+class QAction;
+QT_END_NAMESPACE
+class WebView;
+/*!
+ A proxy object that connects a single browser action
+ to one child webpage action at a time.
+
+ Example usage: used to keep the main window stop action in sync with
+ the current tabs webview's stop action.
+ */
+class WebActionMapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ WebActionMapper(QAction *root, QWebPage::WebAction webAction, QObject *parent);
+ QWebPage::WebAction webAction() const;
+ void addChild(QAction *action);
+ void updateCurrent(QWebPage *currentParent);
+
+private slots:
+ void rootTriggered();
+ void childChanged();
+ void rootDestroyed();
+ void currentDestroyed();
+
+private:
+ QWebPage *m_currentParent;
+ QAction *m_root;
+ QWebPage::WebAction m_webAction;
+};
+
+#include <QtCore/QUrl>
+#include <QtGui/QTabWidget>
+QT_BEGIN_NAMESPACE
+class QCompleter;
+class QLineEdit;
+class QMenu;
+class QStackedWidget;
+QT_END_NAMESPACE
+/*!
+ TabWidget that contains WebViews and a stack widget of associated line edits.
+
+ Connects up the current tab's signals to this class's signal and uses WebActionMapper
+ to proxy the actions.
+ */
+class TabWidget : public QTabWidget
+{
+ Q_OBJECT
+
+signals:
+ // tab widget signals
+ void loadPage(const QString &url);
+ void tabsChanged();
+ void lastTabClosed();
+
+ // current tab signals
+ void setCurrentTitle(const QString &url);
+ void showStatusBarMessage(const QString &message);
+ void linkHovered(const QString &link);
+ void loadProgress(int progress);
+ void geometryChangeRequested(const QRect &geometry);
+ void menuBarVisibilityChangeRequested(bool visible);
+ void statusBarVisibilityChangeRequested(bool visible);
+ void toolBarVisibilityChangeRequested(bool visible);
+ void printRequested(QWebFrame *frame);
+
+public:
+ TabWidget(QWidget *parent = 0);
+ void clear();
+ void addWebAction(QAction *action, QWebPage::WebAction webAction);
+
+ QAction *newTabAction() const;
+ QAction *closeTabAction() const;
+ QAction *recentlyClosedTabsAction() const;
+ QAction *nextTabAction() const;
+ QAction *previousTabAction() const;
+
+ QWidget *lineEditStack() const;
+ QLineEdit *currentLineEdit() const;
+ WebView *currentWebView() const;
+ WebView *webView(int index) const;
+ QLineEdit *lineEdit(int index) const;
+ int webViewIndex(WebView *webView) const;
+
+ QByteArray saveState() const;
+ bool restoreState(const QByteArray &state);
+
+protected:
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void contextMenuEvent(QContextMenuEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+
+public slots:
+ void loadUrlInCurrentTab(const QUrl &url);
+ WebView *newTab(bool makeCurrent = true);
+ void cloneTab(int index = -1);
+ void closeTab(int index = -1);
+ void closeOtherTabs(int index);
+ void reloadTab(int index = -1);
+ void reloadAllTabs();
+ void nextTab();
+ void previousTab();
+
+private slots:
+ void currentChanged(int index);
+ void aboutToShowRecentTabsMenu();
+ void aboutToShowRecentTriggeredAction(QAction *action);
+ void webViewLoadStarted();
+ void webViewIconChanged();
+ void webViewTitleChanged(const QString &title);
+ void webViewUrlChanged(const QUrl &url);
+ void lineEditReturnPressed();
+ void windowCloseRequested();
+ void moveTab(int fromIndex, int toIndex);
+
+private:
+ QAction *m_recentlyClosedTabsAction;
+ QAction *m_newTabAction;
+ QAction *m_closeTabAction;
+ QAction *m_nextTabAction;
+ QAction *m_previousTabAction;
+
+ QMenu *m_recentlyClosedTabsMenu;
+ static const int m_recentlyClosedTabsSize = 10;
+ QList<QUrl> m_recentlyClosedTabs;
+ QList<WebActionMapper*> m_actions;
+
+ QCompleter *m_lineEditCompleter;
+ QStackedWidget *m_lineEdits;
+ TabBar *m_tabBar;
+};
+
+#endif // TABWIDGET_H
+
diff --git a/src/toolbarsearch.cpp b/src/toolbarsearch.cpp
new file mode 100644
index 00000000..16022fe0
--- /dev/null
+++ b/src/toolbarsearch.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "toolbarsearch.h"
+#include "autosaver.h"
+
+#include <QtCore/QSettings>
+#include <QtCore/QUrl>
+
+#include <QtGui/QCompleter>
+#include <QtGui/QMenu>
+#include <QtGui/QStringListModel>
+
+#include <QtWebKit/QWebSettings>
+
+/*
+ ToolbarSearch is a very basic search widget that also contains a small history.
+ Searches are turned into urls that use Google to perform search
+ */
+ToolbarSearch::ToolbarSearch(QWidget *parent)
+ : SearchLineEdit(parent)
+ , m_autosaver(new AutoSaver(this))
+ , m_maxSavedSearches(10)
+ , m_stringListModel(new QStringListModel(this))
+{
+ QMenu *m = menu();
+ connect(m, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMenu()));
+ connect(m, SIGNAL(triggered(QAction*)), this, SLOT(triggeredMenuAction(QAction*)));
+
+ QCompleter *completer = new QCompleter(m_stringListModel, this);
+ completer->setCompletionMode(QCompleter::InlineCompletion);
+ lineEdit()->setCompleter(completer);
+
+ connect(lineEdit(), SIGNAL(returnPressed()), SLOT(searchNow()));
+ setInactiveText(tr("Google"));
+ load();
+}
+
+ToolbarSearch::~ToolbarSearch()
+{
+ m_autosaver->saveIfNeccessary();
+}
+
+void ToolbarSearch::save()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("toolbarsearch"));
+ settings.setValue(QLatin1String("recentSearches"), m_stringListModel->stringList());
+ settings.setValue(QLatin1String("maximumSaved"), m_maxSavedSearches);
+ settings.endGroup();
+}
+
+void ToolbarSearch::load()
+{
+ QSettings settings;
+ settings.beginGroup(QLatin1String("toolbarsearch"));
+ QStringList list = settings.value(QLatin1String("recentSearches")).toStringList();
+ m_maxSavedSearches = settings.value(QLatin1String("maximumSaved"), m_maxSavedSearches).toInt();
+ m_stringListModel->setStringList(list);
+ settings.endGroup();
+}
+
+void ToolbarSearch::searchNow()
+{
+ QString searchText = lineEdit()->text();
+ QStringList newList = m_stringListModel->stringList();
+ if (newList.contains(searchText))
+ newList.removeAt(newList.indexOf(searchText));
+ newList.prepend(searchText);
+ if (newList.size() >= m_maxSavedSearches)
+ newList.removeLast();
+
+ QWebSettings *globalSettings = QWebSettings::globalSettings();
+ if (!globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) {
+ m_stringListModel->setStringList(newList);
+ m_autosaver->changeOccurred();
+ }
+
+ QUrl url(QLatin1String("http://www.google.com/search"));
+ url.addQueryItem(QLatin1String("q"), searchText);
+ url.addQueryItem(QLatin1String("ie"), QLatin1String("UTF-8"));
+ url.addQueryItem(QLatin1String("oe"), QLatin1String("UTF-8"));
+ url.addQueryItem(QLatin1String("client"), QLatin1String("qtdemobrowser"));
+ emit search(url);
+}
+
+void ToolbarSearch::aboutToShowMenu()
+{
+ lineEdit()->selectAll();
+ QMenu *m = menu();
+ m->clear();
+ QStringList list = m_stringListModel->stringList();
+ if (list.isEmpty()) {
+ m->addAction(tr("No Recent Searches"));
+ return;
+ }
+
+ QAction *recent = m->addAction(tr("Recent Searches"));
+ recent->setEnabled(false);
+ for (int i = 0; i < list.count(); ++i) {
+ QString text = list.at(i);
+ m->addAction(text)->setData(text);
+ }
+ m->addSeparator();
+ m->addAction(tr("Clear Recent Searches"), this, SLOT(clear()));
+}
+
+void ToolbarSearch::triggeredMenuAction(QAction *action)
+{
+ QVariant v = action->data();
+ if (v.canConvert<QString>()) {
+ QString text = v.toString();
+ lineEdit()->setText(text);
+ searchNow();
+ }
+}
+
+void ToolbarSearch::clear()
+{
+ m_stringListModel->setStringList(QStringList());
+ m_autosaver->changeOccurred();;
+}
diff --git a/src/toolbarsearch.h b/src/toolbarsearch.h
new file mode 100644
index 00000000..9ecbf183
--- /dev/null
+++ b/src/toolbarsearch.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef TOOLBARSEARCH_H
+#define TOOLBARSEARCH_H
+
+#include "searchlineedit.h"
+
+QT_BEGIN_NAMESPACE
+class QUrl;
+class QAction;
+class QStringListModel;
+QT_END_NAMESPACE
+
+class AutoSaver;
+
+class ToolbarSearch : public SearchLineEdit
+{
+ Q_OBJECT
+
+signals:
+ void search(const QUrl &url);
+
+public:
+ ToolbarSearch(QWidget *parent = 0);
+ ~ToolbarSearch();
+
+public slots:
+ void clear();
+ void searchNow();
+
+private slots:
+ void save();
+ void aboutToShowMenu();
+ void triggeredMenuAction(QAction *action);
+
+private:
+ void load();
+
+ AutoSaver *m_autosaver;
+ int m_maxSavedSearches;
+ QStringListModel *m_stringListModel;
+};
+
+#endif // TOOLBARSEARCH_H
+
diff --git a/src/urllineedit.cpp b/src/urllineedit.cpp
new file mode 100644
index 00000000..47900c04
--- /dev/null
+++ b/src/urllineedit.cpp
@@ -0,0 +1,327 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "urllineedit.h"
+
+#include "browserapplication.h"
+#include "searchlineedit.h"
+#include "webview.h"
+
+#include <QtCore/QEvent>
+
+#include <QtGui/QApplication>
+#include <QtGui/QCompleter>
+#include <QtGui/QFocusEvent>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QLabel>
+#include <QtGui/QLineEdit>
+#include <QtGui/QPainter>
+#include <QtGui/QStyle>
+#include <QtGui/QStyleOptionFrameV2>
+
+#include <QtCore/QDebug>
+
+ExLineEdit::ExLineEdit(QWidget *parent)
+ : QWidget(parent)
+ , m_leftWidget(0)
+ , m_lineEdit(new QLineEdit(this))
+ , m_clearButton(0)
+{
+ setFocusPolicy(m_lineEdit->focusPolicy());
+ setAttribute(Qt::WA_InputMethodEnabled);
+ setSizePolicy(m_lineEdit->sizePolicy());
+ setBackgroundRole(m_lineEdit->backgroundRole());
+ setMouseTracking(true);
+ setAcceptDrops(true);
+ setAttribute(Qt::WA_MacShowFocusRect, true);
+ QPalette p = m_lineEdit->palette();
+ setPalette(p);
+
+ // line edit
+ m_lineEdit->setFrame(false);
+ m_lineEdit->setFocusProxy(this);
+ m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+ QPalette clearPalette = m_lineEdit->palette();
+ clearPalette.setBrush(QPalette::Base, QBrush(Qt::transparent));
+ m_lineEdit->setPalette(clearPalette);
+
+ // clearButton
+ m_clearButton = new ClearButton(this);
+ connect(m_clearButton, SIGNAL(clicked()),
+ m_lineEdit, SLOT(clear()));
+ connect(m_lineEdit, SIGNAL(textChanged(const QString&)),
+ m_clearButton, SLOT(textChanged(const QString&)));
+}
+
+void ExLineEdit::setLeftWidget(QWidget *widget)
+{
+ m_leftWidget = widget;
+}
+
+QWidget *ExLineEdit::leftWidget() const
+{
+ return m_leftWidget;
+}
+
+void ExLineEdit::resizeEvent(QResizeEvent *event)
+{
+ Q_ASSERT(m_leftWidget);
+ updateGeometries();
+ QWidget::resizeEvent(event);
+}
+
+void ExLineEdit::updateGeometries()
+{
+ QStyleOptionFrameV2 panel;
+ initStyleOption(&panel);
+ QRect rect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
+
+ int height = rect.height();
+ int width = rect.width();
+
+ int m_leftWidgetHeight = m_leftWidget->height();
+ m_leftWidget->setGeometry(rect.x() + 2, rect.y() + (height - m_leftWidgetHeight)/2,
+ m_leftWidget->width(), m_leftWidget->height());
+
+ int clearButtonWidth = this->height();
+ m_lineEdit->setGeometry(m_leftWidget->x() + m_leftWidget->width(), 0,
+ width - clearButtonWidth - m_leftWidget->width(), this->height());
+
+ m_clearButton->setGeometry(this->width() - clearButtonWidth, 0,
+ clearButtonWidth, this->height());
+}
+
+void ExLineEdit::initStyleOption(QStyleOptionFrameV2 *option) const
+{
+ option->initFrom(this);
+ option->rect = contentsRect();
+ option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this);
+ option->midLineWidth = 0;
+ option->state |= QStyle::State_Sunken;
+ if (m_lineEdit->isReadOnly())
+ option->state |= QStyle::State_ReadOnly;
+#ifdef QT_KEYPAD_NAVIGATION
+ if (hasEditFocus())
+ option->state |= QStyle::State_HasEditFocus;
+#endif
+ option->features = QStyleOptionFrameV2::None;
+}
+
+QSize ExLineEdit::sizeHint() const
+{
+ m_lineEdit->setFrame(true);
+ QSize size = m_lineEdit->sizeHint();
+ m_lineEdit->setFrame(false);
+ return size;
+}
+
+void ExLineEdit::focusInEvent(QFocusEvent *event)
+{
+ m_lineEdit->event(event);
+ QWidget::focusInEvent(event);
+}
+
+void ExLineEdit::focusOutEvent(QFocusEvent *event)
+{
+ m_lineEdit->event(event);
+
+ if (m_lineEdit->completer()) {
+ connect(m_lineEdit->completer(), SIGNAL(activated(QString)),
+ m_lineEdit, SLOT(setText(QString)));
+ connect(m_lineEdit->completer(), SIGNAL(highlighted(QString)),
+ m_lineEdit, SLOT(_q_completionHighlighted(QString)));
+ }
+ QWidget::focusOutEvent(event);
+}
+
+void ExLineEdit::keyPressEvent(QKeyEvent *event)
+{
+ m_lineEdit->event(event);
+ QWidget::keyPressEvent(event);
+}
+
+bool ExLineEdit::event(QEvent *event)
+{
+ if (event->type() == QEvent::ShortcutOverride)
+ m_lineEdit->event(event);
+ return QWidget::event(event);
+}
+
+void ExLineEdit::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOptionFrameV2 panel;
+ initStyleOption(&panel);
+ style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this);
+}
+
+
+class UrlIconLabel : public QLabel
+{
+
+public:
+ UrlIconLabel(QWidget *parent);
+
+ WebView *m_webView;
+
+protected:
+ void mousePressEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+
+private:
+ QPoint m_dragStartPos;
+
+};
+
+UrlIconLabel::UrlIconLabel(QWidget *parent)
+ : QLabel(parent)
+ , m_webView(0)
+{
+ setMinimumWidth(16);
+ setMinimumHeight(16);
+}
+
+void UrlIconLabel::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ m_dragStartPos = event->pos();
+ QLabel::mousePressEvent(event);
+}
+
+void UrlIconLabel::mouseMoveEvent(QMouseEvent *event)
+{
+ if (event->buttons() == Qt::LeftButton
+ && (event->pos() - m_dragStartPos).manhattanLength() > QApplication::startDragDistance()
+ && m_webView) {
+ QDrag *drag = new QDrag(this);
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setText(m_webView->url().toString());
+ QList<QUrl> urls;
+ urls.append(m_webView->url());
+ mimeData->setUrls(urls);
+ drag->setMimeData(mimeData);
+ drag->exec();
+ }
+}
+
+UrlLineEdit::UrlLineEdit(QWidget *parent)
+ : ExLineEdit(parent)
+ , m_webView(0)
+ , m_iconLabel(0)
+{
+ // icon
+ m_iconLabel = new UrlIconLabel(this);
+ m_iconLabel->resize(16, 16);
+ setLeftWidget(m_iconLabel);
+ m_defaultBaseColor = palette().color(QPalette::Base);
+
+ webViewIconChanged();
+}
+
+void UrlLineEdit::setWebView(WebView *webView)
+{
+ Q_ASSERT(!m_webView);
+ m_webView = webView;
+ m_iconLabel->m_webView = webView;
+ connect(webView, SIGNAL(urlChanged(const QUrl &)),
+ this, SLOT(webViewUrlChanged(const QUrl &)));
+ connect(webView, SIGNAL(loadFinished(bool)),
+ this, SLOT(webViewIconChanged()));
+ connect(webView, SIGNAL(iconChanged()),
+ this, SLOT(webViewIconChanged()));
+ connect(webView, SIGNAL(loadProgress(int)),
+ this, SLOT(update()));
+}
+
+void UrlLineEdit::webViewUrlChanged(const QUrl &url)
+{
+ m_lineEdit->setText(url.toString());
+ m_lineEdit->setCursorPosition(0);
+}
+
+void UrlLineEdit::webViewIconChanged()
+{
+ QUrl url = (m_webView) ? m_webView->url() : QUrl();
+ QIcon icon = BrowserApplication::instance()->icon(url);
+ QPixmap pixmap(icon.pixmap(16, 16));
+ m_iconLabel->setPixmap(pixmap);
+}
+
+QLinearGradient UrlLineEdit::generateGradient(const QColor &color) const
+{
+ QLinearGradient gradient(0, 0, 0, height());
+ gradient.setColorAt(0, m_defaultBaseColor);
+ gradient.setColorAt(0.15, color.lighter(120));
+ gradient.setColorAt(0.5, color);
+ gradient.setColorAt(0.85, color.lighter(120));
+ gradient.setColorAt(1, m_defaultBaseColor);
+ return gradient;
+}
+
+void UrlLineEdit::focusOutEvent(QFocusEvent *event)
+{
+ if (m_lineEdit->text().isEmpty() && m_webView)
+ m_lineEdit->setText(m_webView->url().toString());
+ ExLineEdit::focusOutEvent(event);
+}
+
+void UrlLineEdit::paintEvent(QPaintEvent *event)
+{
+ QPalette p = palette();
+ if (m_webView && m_webView->url().scheme() == QLatin1String("https")) {
+ QColor lightYellow(248, 248, 210);
+ p.setBrush(QPalette::Base, generateGradient(lightYellow));
+ } else {
+ p.setBrush(QPalette::Base, m_defaultBaseColor);
+ }
+ setPalette(p);
+ ExLineEdit::paintEvent(event);
+
+ QPainter painter(this);
+ QStyleOptionFrameV2 panel;
+ initStyleOption(&panel);
+ QRect backgroundRect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
+ if (m_webView && !hasFocus()) {
+ int progress = m_webView->progress();
+ QColor loadingColor = QColor(116, 192, 250);
+ painter.setBrush(generateGradient(loadingColor));
+ painter.setPen(Qt::transparent);
+ int mid = backgroundRect.width() / 100 * progress;
+ QRect progressRect(backgroundRect.x(), backgroundRect.y(), mid, backgroundRect.height());
+ painter.drawRect(progressRect);
+ }
+}
diff --git a/src/urllineedit.h b/src/urllineedit.h
new file mode 100644
index 00000000..283ff48e
--- /dev/null
+++ b/src/urllineedit.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef URLLINEEDIT_H
+#define URLLINEEDIT_H
+
+#include <QtCore/QUrl>
+#include <QtGui/QWidget>
+#include <QtGui/QStyleOptionFrame>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+QT_END_NAMESPACE
+
+class ClearButton;
+class ExLineEdit : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ExLineEdit(QWidget *parent = 0);
+
+ inline QLineEdit *lineEdit() const { return m_lineEdit; }
+
+ void setLeftWidget(QWidget *widget);
+ QWidget *leftWidget() const;
+
+ QSize sizeHint() const;
+
+protected:
+ void focusInEvent(QFocusEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+ void paintEvent(QPaintEvent *event);
+ void resizeEvent(QResizeEvent *event);
+ bool event(QEvent *event);
+
+protected:
+ void updateGeometries();
+ void initStyleOption(QStyleOptionFrameV2 *option) const;
+
+ QWidget *m_leftWidget;
+ QLineEdit *m_lineEdit;
+ ClearButton *m_clearButton;
+};
+
+class UrlIconLabel;
+class WebView;
+class UrlLineEdit : public ExLineEdit
+{
+ Q_OBJECT
+
+public:
+ UrlLineEdit(QWidget *parent = 0);
+ void setWebView(WebView *webView);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+ void focusOutEvent(QFocusEvent *event);
+
+private slots:
+ void webViewUrlChanged(const QUrl &url);
+ void webViewIconChanged();
+
+private:
+ QLinearGradient generateGradient(const QColor &color) const;
+ WebView *m_webView;
+ UrlIconLabel *m_iconLabel;
+ QColor m_defaultBaseColor;
+
+};
+
+
+#endif // URLLINEEDIT_H
+
diff --git a/src/webview.cpp b/src/webview.cpp
new file mode 100644
index 00000000..39cedd95
--- /dev/null
+++ b/src/webview.cpp
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "browserapplication.h"
+#include "browsermainwindow.h"
+#include "cookiejar.h"
+#include "downloadmanager.h"
+#include "networkaccessmanager.h"
+#include "tabwidget.h"
+#include "webview.h"
+
+#include <QtGui/QClipboard>
+#include <QtGui/QMenu>
+#include <QtGui/QMessageBox>
+#include <QtGui/QMouseEvent>
+
+#include <QtWebKit/QWebHitTestResult>
+
+#include <QtUiTools/QUiLoader>
+
+#include <QtCore/QDebug>
+#include <QtCore/QBuffer>
+
+WebPage::WebPage(QObject *parent)
+ : QWebPage(parent)
+ , m_keyboardModifiers(Qt::NoModifier)
+ , m_pressedButtons(Qt::NoButton)
+ , m_openInNewTab(false)
+{
+ setNetworkAccessManager(BrowserApplication::networkAccessManager());
+ connect(this, SIGNAL(unsupportedContent(QNetworkReply *)),
+ this, SLOT(handleUnsupportedContent(QNetworkReply *)));
+}
+
+BrowserMainWindow *WebPage::mainWindow()
+{
+ QObject *w = this->parent();
+ while (w) {
+ if (BrowserMainWindow *mw = qobject_cast<BrowserMainWindow*>(w))
+ return mw;
+ w = w->parent();
+ }
+ return BrowserApplication::instance()->mainWindow();
+}
+
+bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
+{
+ // ctrl open in new tab
+ // ctrl-shift open in new tab and select
+ // ctrl-alt open in new window
+ if (type == QWebPage::NavigationTypeLinkClicked
+ && (m_keyboardModifiers & Qt::ControlModifier
+ || m_pressedButtons == Qt::MidButton)) {
+ bool newWindow = (m_keyboardModifiers & Qt::AltModifier);
+ WebView *webView;
+ if (newWindow) {
+ BrowserApplication::instance()->newMainWindow();
+ BrowserMainWindow *newMainWindow = BrowserApplication::instance()->mainWindow();
+ webView = newMainWindow->currentTab();
+ newMainWindow->raise();
+ newMainWindow->activateWindow();
+ webView->setFocus();
+ } else {
+ bool selectNewTab = (m_keyboardModifiers & Qt::ShiftModifier);
+ webView = mainWindow()->tabWidget()->newTab(selectNewTab);
+ }
+ webView->load(request);
+ m_keyboardModifiers = Qt::NoModifier;
+ m_pressedButtons = Qt::NoButton;
+ return false;
+ }
+ if (frame == mainFrame()) {
+ m_loadingUrl = request.url();
+ emit loadingUrl(m_loadingUrl);
+ }
+ return QWebPage::acceptNavigationRequest(frame, request, type);
+}
+
+QWebPage *WebPage::createWindow(QWebPage::WebWindowType type)
+{
+ Q_UNUSED(type);
+ if (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)
+ m_openInNewTab = true;
+ if (m_openInNewTab) {
+ m_openInNewTab = false;
+ return mainWindow()->tabWidget()->newTab()->page();
+ }
+ BrowserApplication::instance()->newMainWindow();
+ BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow();
+ return mainWindow->currentTab()->page();
+}
+
+#if !defined(QT_NO_UITOOLS)
+QObject *WebPage::createPlugin(const QString &classId, const QUrl &url, const QStringList &paramNames, const QStringList &paramValues)
+{
+ Q_UNUSED(url);
+ Q_UNUSED(paramNames);
+ Q_UNUSED(paramValues);
+ QUiLoader loader;
+ return loader.createWidget(classId, view());
+}
+#endif // !defined(QT_NO_UITOOLS)
+
+void WebPage::handleUnsupportedContent(QNetworkReply *reply)
+{
+ if (reply->error() == QNetworkReply::NoError) {
+ BrowserApplication::downloadManager()->handleUnsupportedContent(reply);
+ return;
+ }
+
+ QFile file(QLatin1String(":/notfound.html"));
+ bool isOpened = file.open(QIODevice::ReadOnly);
+ Q_ASSERT(isOpened);
+ QString title = tr("Error loading page: %1").arg(reply->url().toString());
+ QString html = QString(QLatin1String(file.readAll()))
+ .arg(title)
+ .arg(reply->errorString())
+ .arg(reply->url().toString());
+
+ QBuffer imageBuffer;
+ imageBuffer.open(QBuffer::ReadWrite);
+ QIcon icon = view()->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, view());
+ QPixmap pixmap = icon.pixmap(QSize(32,32));
+ if (pixmap.save(&imageBuffer, "PNG")) {
+ html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"),
+ QString(QLatin1String(imageBuffer.buffer().toBase64())));
+ }
+
+ QList<QWebFrame*> frames;
+ frames.append(mainFrame());
+ while (!frames.isEmpty()) {
+ QWebFrame *frame = frames.takeFirst();
+ if (frame->url() == reply->url()) {
+ frame->setHtml(html, reply->url());
+ return;
+ }
+ QList<QWebFrame *> children = frame->childFrames();
+ foreach(QWebFrame *frame, children)
+ frames.append(frame);
+ }
+ if (m_loadingUrl == reply->url()) {
+ mainFrame()->setHtml(html, reply->url());
+ }
+}
+
+
+WebView::WebView(QWidget* parent)
+ : QWebView(parent)
+ , m_progress(0)
+ , m_page(new WebPage(this))
+{
+ setPage(m_page);
+ connect(page(), SIGNAL(statusBarMessage(const QString&)),
+ SLOT(setStatusBarText(const QString&)));
+ connect(this, SIGNAL(loadProgress(int)),
+ this, SLOT(setProgress(int)));
+ connect(this, SIGNAL(loadFinished(bool)),
+ this, SLOT(loadFinished()));
+ connect(page(), SIGNAL(loadingUrl(const QUrl&)),
+ this, SIGNAL(urlChanged(const QUrl &)));
+ connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)),
+ this, SLOT(downloadRequested(const QNetworkRequest &)));
+ page()->setForwardUnsupportedContent(true);
+
+}
+
+void WebView::contextMenuEvent(QContextMenuEvent *event)
+{
+ QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos());
+ if (!r.linkUrl().isEmpty()) {
+ QMenu menu(this);
+ menu.addAction(pageAction(QWebPage::OpenLinkInNewWindow));
+ menu.addAction(tr("Open in New Tab"), this, SLOT(openLinkInNewTab()));
+ menu.addSeparator();
+ menu.addAction(pageAction(QWebPage::DownloadLinkToDisk));
+ // Add link to bookmarks...
+ menu.addSeparator();
+ menu.addAction(pageAction(QWebPage::CopyLinkToClipboard));
+ if (page()->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled))
+ menu.addAction(pageAction(QWebPage::InspectElement));
+ menu.exec(mapToGlobal(event->pos()));
+ return;
+ }
+ QWebView::contextMenuEvent(event);
+}
+
+void WebView::wheelEvent(QWheelEvent *event)
+{
+ if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
+ int numDegrees = event->delta() / 8;
+ int numSteps = numDegrees / 15;
+ setTextSizeMultiplier(textSizeMultiplier() + numSteps * 0.1);
+ event->accept();
+ return;
+ }
+ QWebView::wheelEvent(event);
+}
+
+void WebView::openLinkInNewTab()
+{
+ m_page->m_openInNewTab = true;
+ pageAction(QWebPage::OpenLinkInNewWindow)->trigger();
+}
+
+void WebView::setProgress(int progress)
+{
+ m_progress = progress;
+}
+
+void WebView::loadFinished()
+{
+ if (100 != m_progress) {
+ qWarning() << "Recieved finished signal while progress is still:" << progress()
+ << "Url:" << url();
+ }
+ m_progress = 0;
+}
+
+void WebView::loadUrl(const QUrl &url)
+{
+ m_initialUrl = url;
+ load(url);
+}
+
+QString WebView::lastStatusBarText() const
+{
+ return m_statusBarText;
+}
+
+QUrl WebView::url() const
+{
+ QUrl url = QWebView::url();
+ if (!url.isEmpty())
+ return url;
+
+ return m_initialUrl;
+}
+
+void WebView::mousePressEvent(QMouseEvent *event)
+{
+ m_page->m_pressedButtons = event->buttons();
+ m_page->m_keyboardModifiers = event->modifiers();
+ QWebView::mousePressEvent(event);
+}
+
+void WebView::mouseReleaseEvent(QMouseEvent *event)
+{
+ QWebView::mouseReleaseEvent(event);
+ if (!event->isAccepted() && (m_page->m_pressedButtons & Qt::MidButton)) {
+ QUrl url(QApplication::clipboard()->text(QClipboard::Selection));
+ if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) {
+ setUrl(url);
+ }
+ }
+}
+
+void WebView::setStatusBarText(const QString &string)
+{
+ m_statusBarText = string;
+}
+
+void WebView::downloadRequested(const QNetworkRequest &request)
+{
+ BrowserApplication::downloadManager()->download(request);
+}
diff --git a/src/webview.h b/src/webview.h
new file mode 100644
index 00000000..73c9dafb
--- /dev/null
+++ b/src/webview.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef WEBVIEW_H
+#define WEBVIEW_H
+
+#include <QtWebKit/QWebView>
+
+QT_BEGIN_NAMESPACE
+class QAuthenticator;
+class QMouseEvent;
+class QNetworkProxy;
+class QNetworkReply;
+class QSslError;
+QT_END_NAMESPACE
+
+class BrowserMainWindow;
+class WebPage : public QWebPage {
+ Q_OBJECT
+
+signals:
+ void loadingUrl(const QUrl &url);
+
+public:
+ WebPage(QObject *parent = 0);
+ BrowserMainWindow *mainWindow();
+
+protected:
+ bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type);
+ QWebPage *createWindow(QWebPage::WebWindowType type);
+#if !defined(QT_NO_UITOOLS)
+ QObject *createPlugin(const QString &classId, const QUrl &url, const QStringList &paramNames, const QStringList &paramValues);
+#endif
+
+private slots:
+ void handleUnsupportedContent(QNetworkReply *reply);
+
+private:
+ friend class WebView;
+
+ // set the webview mousepressedevent
+ Qt::KeyboardModifiers m_keyboardModifiers;
+ Qt::MouseButtons m_pressedButtons;
+ bool m_openInNewTab;
+ QUrl m_loadingUrl;
+};
+
+class WebView : public QWebView {
+ Q_OBJECT
+
+public:
+ WebView(QWidget *parent = 0);
+ WebPage *webPage() const { return m_page; }
+
+ void loadUrl(const QUrl &url);
+ QUrl url() const;
+
+ QString lastStatusBarText() const;
+ inline int progress() const { return m_progress; }
+
+protected:
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void contextMenuEvent(QContextMenuEvent *event);
+ void wheelEvent(QWheelEvent *event);
+
+private slots:
+ void setProgress(int progress);
+ void loadFinished();
+ void setStatusBarText(const QString &string);
+ void downloadRequested(const QNetworkRequest &request);
+ void openLinkInNewTab();
+
+private:
+ QString m_statusBarText;
+ QUrl m_initialUrl;
+ int m_progress;
+ WebPage *m_page;
+};
+
+#endif
diff --git a/src/xbel.cpp b/src/xbel.cpp
new file mode 100644
index 00000000..3accd76d
--- /dev/null
+++ b/src/xbel.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include "xbel.h"
+
+#include <QtCore/QFile>
+
+BookmarkNode::BookmarkNode(BookmarkNode::Type type, BookmarkNode *parent) :
+ expanded(false)
+ , m_parent(parent)
+ , m_type(type)
+{
+ if (parent)
+ parent->add(this);
+}
+
+BookmarkNode::~BookmarkNode()
+{
+ if (m_parent)
+ m_parent->remove(this);
+ qDeleteAll(m_children);
+ m_parent = 0;
+ m_type = BookmarkNode::Root;
+}
+
+bool BookmarkNode::operator==(const BookmarkNode &other)
+{
+ if (url != other.url
+ || title != other.title
+ || desc != other.desc
+ || expanded != other.expanded
+ || m_type != other.m_type
+ || m_children.count() != other.m_children.count())
+ return false;
+
+ for (int i = 0; i < m_children.count(); ++i)
+ if (!((*(m_children[i])) == (*(other.m_children[i]))))
+ return false;
+ return true;
+}
+
+BookmarkNode::Type BookmarkNode::type() const
+{
+ return m_type;
+}
+
+void BookmarkNode::setType(Type type)
+{
+ m_type = type;
+}
+
+QList<BookmarkNode *> BookmarkNode::children() const
+{
+ return m_children;
+}
+
+BookmarkNode *BookmarkNode::parent() const
+{
+ return m_parent;
+}
+
+void BookmarkNode::add(BookmarkNode *child, int offset)
+{
+ Q_ASSERT(child->m_type != Root);
+ if (child->m_parent)
+ child->m_parent->remove(child);
+ child->m_parent = this;
+ if (-1 == offset)
+ offset = m_children.size();
+ m_children.insert(offset, child);
+}
+
+void BookmarkNode::remove(BookmarkNode *child)
+{
+ child->m_parent = 0;
+ m_children.removeAll(child);
+}
+
+
+XbelReader::XbelReader()
+{
+}
+
+BookmarkNode *XbelReader::read(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.exists()) {
+ return new BookmarkNode(BookmarkNode::Root);
+ }
+ file.open(QFile::ReadOnly);
+ return read(&file);
+}
+
+BookmarkNode *XbelReader::read(QIODevice *device)
+{
+ BookmarkNode *root = new BookmarkNode(BookmarkNode::Root);
+ setDevice(device);
+ while (!atEnd()) {
+ readNext();
+ if (isStartElement()) {
+ QString version = attributes().value(QLatin1String("version")).toString();
+ if (name() == QLatin1String("xbel")
+ && (version.isEmpty() || version == QLatin1String("1.0"))) {
+ readXBEL(root);
+ } else {
+ raiseError(QObject::tr("The file is not an XBEL version 1.0 file."));
+ }
+ }
+ }
+ return root;
+}
+
+void XbelReader::readXBEL(BookmarkNode *parent)
+{
+ Q_ASSERT(isStartElement() && name() == QLatin1String("xbel"));
+
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement())
+ break;
+
+ if (isStartElement()) {
+ if (name() == QLatin1String("folder"))
+ readFolder(parent);
+ else if (name() == QLatin1String("bookmark"))
+ readBookmarkNode(parent);
+ else if (name() == QLatin1String("separator"))
+ readSeparator(parent);
+ else
+ skipUnknownElement();
+ }
+ }
+}
+
+void XbelReader::readFolder(BookmarkNode *parent)
+{
+ Q_ASSERT(isStartElement() && name() == QLatin1String("folder"));
+
+ BookmarkNode *folder = new BookmarkNode(BookmarkNode::Folder, parent);
+ folder->expanded = (attributes().value(QLatin1String("folded")) == QLatin1String("no"));
+
+ while (!atEnd()) {
+ readNext();
+
+ if (isEndElement())
+ break;
+
+ if (isStartElement()) {
+ if (name() == QLatin1String("title"))
+ readTitle(folder);
+ else if (name() == QLatin1String("desc"))
+ readDescription(folder);
+ else if (name() == QLatin1String("folder"))
+ readFolder(folder);
+ else if (name() == QLatin1String("bookmark"))
+ readBookmarkNode(folder);
+ else if (name() == QLatin1String("separator"))
+ readSeparator(folder);
+ else
+ skipUnknownElement();
+ }
+ }
+}
+
+void XbelReader::readTitle(BookmarkNode *parent)
+{
+ Q_ASSERT(isStartElement() && name() == QLatin1String("title"));
+ parent->title = readElementText();
+}
+
+void XbelReader::readDescription(BookmarkNode *parent)
+{
+ Q_ASSERT(isStartElement() && name() == QLatin1String("desc"));
+ parent->desc = readElementText();
+}
+
+void XbelReader::readSeparator(BookmarkNode *parent)
+{
+ new BookmarkNode(BookmarkNode::Separator, parent);
+ // empty elements have a start and end element
+ readNext();
+}
+
+void XbelReader::readBookmarkNode(BookmarkNode *parent)
+{
+ Q_ASSERT(isStartElement() && name() == QLatin1String("bookmark"));
+ BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark, parent);
+ bookmark->url = attributes().value(QLatin1String("href")).toString();
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement())
+ break;
+
+ if (isStartElement()) {
+ if (name() == QLatin1String("title"))
+ readTitle(bookmark);
+ else if (name() == QLatin1String("desc"))
+ readDescription(bookmark);
+ else
+ skipUnknownElement();
+ }
+ }
+ if (bookmark->title.isEmpty())
+ bookmark->title = QObject::tr("Unknown title");
+}
+
+void XbelReader::skipUnknownElement()
+{
+ Q_ASSERT(isStartElement());
+
+ while (!atEnd()) {
+ readNext();
+
+ if (isEndElement())
+ break;
+
+ if (isStartElement())
+ skipUnknownElement();
+ }
+}
+
+
+XbelWriter::XbelWriter()
+{
+ setAutoFormatting(true);
+}
+
+bool XbelWriter::write(const QString &fileName, const BookmarkNode *root)
+{
+ QFile file(fileName);
+ if (!root || !file.open(QFile::WriteOnly))
+ return false;
+ return write(&file, root);
+}
+
+bool XbelWriter::write(QIODevice *device, const BookmarkNode *root)
+{
+ setDevice(device);
+
+ writeStartDocument();
+ writeDTD(QLatin1String("<!DOCTYPE xbel>"));
+ writeStartElement(QLatin1String("xbel"));
+ writeAttribute(QLatin1String("version"), QLatin1String("1.0"));
+ if (root->type() == BookmarkNode::Root) {
+ for (int i = 0; i < root->children().count(); ++i)
+ writeItem(root->children().at(i));
+ } else {
+ writeItem(root);
+ }
+
+ writeEndDocument();
+ return true;
+}
+
+void XbelWriter::writeItem(const BookmarkNode *parent)
+{
+ switch (parent->type()) {
+ case BookmarkNode::Folder:
+ writeStartElement(QLatin1String("folder"));
+ writeAttribute(QLatin1String("folded"), parent->expanded ? QLatin1String("no") : QLatin1String("yes"));
+ writeTextElement(QLatin1String("title"), parent->title);
+ for (int i = 0; i < parent->children().count(); ++i)
+ writeItem(parent->children().at(i));
+ writeEndElement();
+ break;
+ case BookmarkNode::Bookmark:
+ writeStartElement(QLatin1String("bookmark"));
+ if (!parent->url.isEmpty())
+ writeAttribute(QLatin1String("href"), parent->url);
+ writeTextElement(QLatin1String("title"), parent->title);
+ if (!parent->desc.isEmpty())
+ writeAttribute(QLatin1String("desc"), parent->desc);
+ writeEndElement();
+ break;
+ case BookmarkNode::Separator:
+ writeEmptyElement(QLatin1String("separator"));
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/xbel.h b/src/xbel.h
new file mode 100644
index 00000000..fdacd1b9
--- /dev/null
+++ b/src/xbel.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information
+** to ensure GNU General Public Licensing requirements will be met:
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
+** exception, Nokia gives you certain additional rights. These rights
+** are described in the Nokia Qt GPL Exception version 1.3, included in
+** the file GPL_EXCEPTION.txt in this package.
+**
+** Qt for Windows(R) Licensees
+** As a special exception, Nokia, as the sole copyright holder for Qt
+** Designer, grants users of the Qt/Eclipse Integration plug-in the
+** right for the Qt/Eclipse Integration to link to functionality
+** provided by Qt Designer and its related libraries.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#ifndef XBEL_H
+#define XBEL_H
+
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QDateTime>
+
+class BookmarkNode
+{
+public:
+ enum Type {
+ Root,
+ Folder,
+ Bookmark,
+ Separator
+ };
+
+ BookmarkNode(Type type = Root, BookmarkNode *parent = 0);
+ ~BookmarkNode();
+ bool operator==(const BookmarkNode &other);
+
+ Type type() const;
+ void setType(Type type);
+ QList<BookmarkNode *> children() const;
+ BookmarkNode *parent() const;
+
+ void add(BookmarkNode *child, int offset = -1);
+ void remove(BookmarkNode *child);
+
+ QString url;
+ QString title;
+ QString desc;
+ bool expanded;
+
+private:
+ BookmarkNode *m_parent;
+ Type m_type;
+ QList<BookmarkNode *> m_children;
+
+};
+
+class XbelReader : public QXmlStreamReader
+{
+public:
+ XbelReader();
+ BookmarkNode *read(const QString &fileName);
+ BookmarkNode *read(QIODevice *device);
+
+private:
+ void skipUnknownElement();
+ void readXBEL(BookmarkNode *parent);
+ void readTitle(BookmarkNode *parent);
+ void readDescription(BookmarkNode *parent);
+ void readSeparator(BookmarkNode *parent);
+ void readFolder(BookmarkNode *parent);
+ void readBookmarkNode(BookmarkNode *parent);
+};
+
+#include <QtCore/QXmlStreamWriter>
+
+class XbelWriter : public QXmlStreamWriter
+{
+public:
+ XbelWriter();
+ bool write(const QString &fileName, const BookmarkNode *root);
+ bool write(QIODevice *device, const BookmarkNode *root);
+
+private:
+ void writeItem(const BookmarkNode *parent);
+};
+
+#endif // XBEL_H
+