changeset 45:71b382c10ef6

add coarse and incomplete QT browser port
author mandel
date Sun, 17 May 2009 18:49:59 +0000
parents 3cb15c92ac28
children fd6eb3a1759d
files demos/browser/TODO.txt demos/browser/autosaver.d demos/browser/bookmarks.d demos/browser/browser.icns demos/browser/browser.ico demos/browser/browserapplication.d demos/browser/browsermainwindow.d demos/browser/chasewidget.d demos/browser/cookiejar.d demos/browser/data/addtab.png demos/browser/data/browser.svg demos/browser/data/closetab.png demos/browser/data/data.qrc demos/browser/data/defaultbookmarks.xbel demos/browser/data/defaulticon.png demos/browser/data/history.png demos/browser/data/loading.gif demos/browser/downloadmanager.d demos/browser/edittableview.d demos/browser/edittreeview.d demos/browser/history.d demos/browser/htmls/htmls.qrc demos/browser/htmls/notfound.html demos/browser/main.d demos/browser/modelmenu.d demos/browser/networkaccessmanager.d demos/browser/searchlineedit.d demos/browser/settings.d demos/browser/squeezelabel.d demos/browser/tabwidget.d demos/browser/toolbarsearch.d demos/browser/urllineedit.d demos/browser/webview.d demos/browser/xbel.d
diffstat 34 files changed, 10192 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/TODO.txt	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,13 @@
+This is a very coarse port of ./demos/browser from the QT 4.5.1 linux sources.
+
+Done:
+- *.cpp and *.h files have been merged into *.d files
+- C preprocessor include lines where replaced by import lines.
+- const from function headers were removed.
+- some '::' and most '->' have been replaced by '.'
+
+Todo:
+- *.ui files need to converted to source files and translated to D
+- imports need to be corrected
+- module declarations are probably missing
+- everything else...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/autosaver.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module autosaver;
+
+import QtCore.QObject;
+import QtCore.QBasicTimer;
+import QtCore.QTime;
+
+import QtCore.QDir;
+import QtCore.QCoreApplication;
+import QtCore.QMetaObject;
+import QtDebug;
+
+const uint AUTOSAVE_IN  = 1000 * 3  // seconds
+const uint MAXWAIT = 1000 * 15 // seconds
+
+/*
+    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:
+    this(QObject *parent)
+{
+	super(parent);
+    Q_ASSERT(parent);
+}
+    ~this();
+{
+    if (m_timer.isActive())
+        qWarning() << "AutoSaver: still active when destroyed, changes not saved.";
+}
+    void 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";
+    }
+}
+
+public slots:
+    void changeOccurred();
+{
+    if (m_firstChange.isNull())
+        m_firstChange.start();
+
+    if (m_firstChange.elapsed() > MAXWAIT) {
+        saveIfNeccessary();
+    } else {
+        m_timer.start(AUTOSAVE_IN, this);
+    }
+}
+
+protected:
+    void timerEvent(QTimerEvent *event)
+{
+    if (event.timerId() == m_timer.timerId()) {
+        saveIfNeccessary();
+    } else {
+        QObject::timerEvent(event);
+    }
+}
+
+
+private:
+    QBasicTimer m_timer;
+    QTime m_firstChange;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/bookmarks.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,1170 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtCore.QObject;
+import QtCore.QAbstractItemModel;
+
+import QtGui.QUndoCommand;
+
+
+import bookmarks;
+
+import autosaver;
+import browserapplication;
+import history;
+import xbel;
+
+import QtCore.QBuffer;
+import QtCore.QFile;
+import QtCore.QMimeData;
+
+import QtGui.QDesktopServices;
+import QtGui.QDragEnterEvent;
+import QtGui.QFileDialog;
+import QtGui.QHeaderView;
+import QtGui.QIcon;
+import QtGui.QMessageBox;
+import QtGui.QToolButton;
+
+import QtWebKit.QWebSettings;
+
+import QtCore.QDebug;
+
+const char[] BOOKMARKBAR = "Bookmarks Bar";
+const char[] BOOKMARKMENU = "Bookmarks Menu";
+
+
+/*!
+    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 = null)
+{
+	super(parent);
+	m_loaded = false;
+	m_saveTimer = new AutoSaver(this);
+	m_bookmarkRootNode = null;
+	m_bookmarkModel = null;
+    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()));
+}
+
+
+    ~this()
+{
+    m_saveTimer.saveIfNeccessary();
+}
+
+
+    void addBookmark(BookmarkNode *parent, BookmarkNode *node, int row = -1)
+{
+    if (!m_loaded)
+        return;
+    Q_ASSERT(parent);
+    InsertBookmarksCommand *command = new InsertBookmarksCommand(this, parent, node, row);
+    m_commands.push(command);
+}
+
+
+    void 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 setTitle(BookmarkNode *node, QString &newTitle);
+{
+    if (!m_loaded)
+        return;
+
+    Q_ASSERT(node);
+    ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newTitle, true);
+    m_commands.push(command);
+}
+
+
+    void setUrl(BookmarkNode *node, QString &newUrl);
+{
+    if (!m_loaded)
+        return;
+
+    Q_ASSERT(node);
+    ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newUrl, false);
+    m_commands.push(command);
+}
+
+
+void changeExpanded()
+{
+    m_saveTimer.changeOccurred();
+}
+
+    BookmarkNode *bookmarks();
+{
+    if (!m_loaded)
+        load();
+    return m_bookmarkRootNode;
+}
+
+    BookmarkNode *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 *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* bookmarksModel()
+{
+    if (!m_bookmarkModel)
+        m_bookmarkModel = new BookmarksModel(this, this);
+    return m_bookmarkModel;
+}
+
+    QUndoStack *undoRedoStack() { return &m_commands; };
+
+public slots:
+    void 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 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"));
+}
+
+private slots:
+    void save()
+{
+    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;
+}
+
+private:
+    void 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 = null;
+    BookmarkNode *menu = null;
+    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));
+}
+
+
+    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:
+    this(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row)
+{
+	super(BookmarksManager::tr("Remove Bookmark"))
+	m_row = row;
+	m_bookmarkManagaer = m_bookmarkManagaer;
+	m_node = parent.children().value(row);
+	m_parent = parent;
+	m_done = false;
+}
+
+~this()
+{
+    if (m_done && !m_node.parent()) {
+        delete m_node;
+    }
+}
+
+void undo()
+{
+    m_parent.add(m_node, m_row);
+    emit m_bookmarkManagaer.entryAdded(m_node);
+    m_done = false;
+}
+
+void redo()
+{
+    m_parent.remove(m_node);
+    emit m_bookmarkManagaer.entryRemoved(m_parent, m_row, m_node);
+    m_done = true;
+}
+
+protected:
+    int m_row;
+    BookmarksManager *m_bookmarkManagaer;
+    BookmarkNode *m_node;
+    BookmarkNode *m_parent;
+    bool m_done;
+};
+
+class InsertBookmarksCommand : public RemoveBookmarksCommand
+{
+
+public:
+this(BookmarksManager *m_bookmarkManagaer,
+                BookmarkNode *parent, BookmarkNode *node, int row)
+{
+	super(m_bookmarkManagaer, parent, row);
+	
+    setText(BookmarksManager::tr("Insert Bookmark"));
+    m_node = node;
+}
+
+    void undo() { RemoveBookmarksCommand::redo(); }
+    void redo() { RemoveBookmarksCommand::undo(); }
+}
+
+class ChangeBookmarkCommand : public QUndoCommand
+{
+
+public:
+    this(BookmarksManager *m_bookmarkManagaer,
+        BookmarkNode *node, QString &newValue, bool title)
+{
+	super();
+	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 undo()
+{
+    if (m_title)
+        m_node.title = m_oldValue;
+    else
+        m_node.url = m_oldValue;
+    emit m_bookmarkManagaer.entryChanged(m_node);
+}
+
+void redo()
+{
+    if (m_title)
+        m_node.title = m_newValue;
+    else
+        m_node.url = m_newValue;
+    emit m_bookmarkManagaer.entryChanged(m_node);
+}
+
+private:
+    BookmarksManager *m_bookmarkManagaer;
+    bool m_title;
+    QString m_oldValue;
+    QString m_newValue;
+    BookmarkNode *m_node;
+}
+
+/*!
+    BookmarksModel is a QAbstractItemModel wrapper around the BookmarkManager
+  */
+import QtGui/QIcon;
+class BookmarksModel : public QAbstractItemModel
+{
+    Q_OBJECT
+
+public slots:
+    void 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 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 entryChanged(BookmarkNode *item);
+{
+    QModelIndex idx = index(item);
+    emit dataChanged(idx, idx);
+}
+public:
+    enum Roles {
+        TypeRole = Qt.UserRole + 1,
+        UrlRole = Qt.UserRole + 2,
+        UrlStringRole = Qt.UserRole + 3,
+        SeparatorRole = Qt.UserRole + 4
+    };
+
+    BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent = null)
+{
+	super(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 *)));
+}
+    
+    
+    inline BookmarksManager *bookmarksManager() { return m_bookmarksManager; }
+
+    QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole);
+{
+    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 data(QModelIndex &index, int role = Qt.DisplayRole)
+{
+    if (!index.isValid() || index.model() != this)
+        return QVariant();
+
+    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 columnCount(QModelIndex &parent = QModelIndex())
+{
+    return (parent.column() > 0) ? 0 : 2;
+}
+
+    int rowCount(QModelIndex &parent = QModelIndex())
+{
+    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 index(int, int, QModelIndex& = QModelIndex())
+{
+    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 parent(QModelIndex& index= QModelIndex())
+{
+    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);
+}
+
+
+    Qt.ItemFlags flags(QModelIndex &index)
+{
+    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 supportedDropActions ();
+{
+    return Qt.CopyAction | Qt.MoveAction;
+}
+
+
+    bool removeRows(int row, int count, QModelIndex &parent = QModelIndex());
+{
+    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;
+}
+    
+    bool setData(QModelIndex &index, QVariant &value, int role = Qt.EditRole)
+{
+    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;
+}
+
+
+    QMimeData *mimeData(QModelIndexList &indexes);
+{
+    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;
+}
+
+
+#define MIMETYPE QLatin1String("application/bookmarks.xbel")
+
+QStringList mimeTypes()
+{
+    QStringList types;
+    types << MIMETYPE;
+    return types;
+}
+
+bool dropMimeData(QMimeData *data,  Qt.DropAction action, int row, int column, 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 hasChildren(QModelIndex &parent = QModelIndex())
+{
+    if (!parent.isValid())
+        return true;
+    const BookmarkNode *parentNode = node(parent);
+    return (parentNode.type() == BookmarkNode::Folder);
+}
+
+    BookmarkNode *node(QModelIndex &index)
+{
+    BookmarkNode *itemNode = static_cast<BookmarkNode*>(index.internalPointer());
+    if (!itemNode)
+        return m_bookmarksManager.bookmarks();
+    return itemNode;
+}
+
+    QModelIndex index(BookmarkNode *node)
+{
+    BookmarkNode *parent = node.parent();
+    if (!parent)
+        return QModelIndex();
+    return createIndex(parent.children().indexOf(node), 0, node);
+}
+
+private:
+
+    bool m_endMacro;
+    BookmarksManager *m_bookmarksManager;
+}
+
+// Menu that is dynamically populated from the bookmarks
+import modelmenu;
+class BookmarksMenu : public ModelMenu
+{
+    Q_OBJECT
+
+signals:
+    void openUrl(QUrl &url);
+
+public:
+     BookmarksMenu(QWidget *parent = null)
+{
+super(parent);	
+	m_bookmarksManager = 0;
+    connect(this, SIGNAL(activated(QModelIndex &)),
+            this, SLOT(activated(QModelIndex &)));
+    setMaxRows(-1);
+    setHoverRole(BookmarksModel::UrlStringRole);
+    setSeparatorRole(BookmarksModel::SeparatorRole);
+}
+     void setInitialActions(QList<QAction*> actions);
+{
+    m_initialActions = actions;
+    for (int i = 0; i < m_initialActions.count(); ++i)
+        addAction(m_initialActions.at(i));
+}
+protected:
+	bool 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;
+}
+
+private slots:
+    void activated(QModelIndex &index)
+{
+    emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
+}
+
+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.
+ */
+import QtGui/QSortFilterProxyModel;
+class AddBookmarkProxyModel : public QSortFilterProxyModel
+{
+    Q_OBJECT
+public:
+    this(QObject * parent = null)
+{
+	super(parent);
+}
+    int columnCount(QModelIndex & parent = QModelIndex())
+{
+    return qMin(1, QSortFilterProxyModel::columnCount(parent));
+}
+protected:
+    bool filterAcceptsRow(int source_row, QModelIndex &source_parent)
+{
+    QModelIndex idx = sourceModel().index(source_row, 0, source_parent);
+    return sourceModel().hasChildren(idx);
+}
+}
+
+/*!
+    Add bookmark dialog
+ */
+import ui_addbookmarkdialog;
+class AddBookmarkDialog : public QDialog, public Ui_AddBookmarkDialog
+{
+    Q_OBJECT
+
+public:
+    this(QString &url, QString &title, QWidget *parent = null, BookmarksManager *bookmarkManager = null)
+ : 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);
+}
+private slots:
+	
+void 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();
+}
+
+private:
+    QString m_url;
+    BookmarksManager *m_bookmarksManager;
+    AddBookmarkProxyModel *m_proxyModel;
+}
+
+import ui_bookmarks;
+
+//class TreeProxyModel;
+class BookmarksDialog : public QDialog, public Ui_BookmarksDialog
+{
+    Q_OBJECT
+
+signals:
+    void openUrl(QUrl &url);
+
+public:
+    this(QWidget *parent = null, BookmarksManager *manager = null)
+    : 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(QModelIndex&)),
+            this, SLOT(open()));
+    tree.setContextMenuPolicy(Qt.CustomContextMenu);
+    connect(tree, SIGNAL(customContextMenuRequested(QPoint &)),
+            this, SLOT(customContextMenuRequested(QPoint &)));
+    connect(addFolderButton, SIGNAL(clicked()),
+            this, SLOT(newFolder()));
+    expandNodes(m_bookmarksManager.bookmarks());
+    setAttribute(Qt.WA_DeleteOnClose);
+}
+    ~this()
+{
+    if (saveExpandedNodes(tree.rootIndex()))
+        m_bookmarksManager.changeExpanded();
+}
+
+private slots:
+    void customContextMenuRequested(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 open()
+{
+    QModelIndex index = tree.currentIndex();
+    if (!index.parent().isValid())
+        return;
+    emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel::UrlRole).toUrl());
+}
+
+    void 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);
+}
+
+private:
+    void 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);
+        }
+    }
+}
+
+bool saveExpandedNodes(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;
+}
+
+    BookmarksManager *m_bookmarksManager;
+    BookmarksModel *m_bookmarksModel;
+    TreeProxyModel *m_proxyModel;
+};
+
+import QtGui/QToolBar;
+class BookmarksToolBar : public QToolBar
+{
+    Q_OBJECT
+
+signals:
+    void openUrl(QUrl &url);
+
+public:
+    BookmarksToolBar(BookmarksModel *model, QWidget *parent = null)
+{
+	super(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(QModelIndex &, int, int)), this, SLOT(build()));
+    connect(m_bookmarksModel, SIGNAL(rowsRemoved(QModelIndex &, int, int)), this, SLOT(build()));
+    connect(m_bookmarksModel, SIGNAL(dataChanged(QModelIndex &, QModelIndex &)), this, SLOT(build()));
+    setAcceptDrops(true);
+}
+
+    void setRootIndex(QModelIndex &index);
+{
+    m_root = index;
+    build();
+}
+
+    QModelIndex rootIndex()
+{
+    return m_root;
+}
+
+protected:
+    void dragEnterEvent(QDragEnterEvent *event)
+{
+    const QMimeData *mimeData = event.mimeData();
+    if (mimeData.hasUrls())
+        event.acceptProposedAction();
+    QToolBar::dragEnterEvent(event);
+}
+
+    void 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);
+}
+
+private slots:
+    void triggered(QAction *action)
+{
+    QVariant v = action.data();
+    if (v.canConvert<QUrl>()) {
+        emit openUrl(v.toUrl());
+    }
+}
+
+    void activated(QModelIndex &index)
+{
+    emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
+}
+
+
+    void 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(QModelIndex &)),
+                    this, SLOT(activated(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));
+        }
+    }
+}
+
+private:
+    BookmarksModel *m_bookmarksModel;
+    QPersistentModelIndex m_root;
+}
Binary file demos/browser/browser.icns has changed
Binary file demos/browser/browser.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/browserapplication.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,507 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtGui.QApplication;
+
+import QtCore.QUrl;
+import QtCore.QPointer;
+
+import QtGui.QIcon;
+
+import browserapplication;
+
+import bookmarks;
+import browsermainwindow;
+import cookiejar;
+import downloadmanager;
+import history;
+import networkaccessmanager;
+import tabwidget;
+import webview;
+
+import QtCore.QBuffer;
+import QtCore.QDir;
+import QtCore.QLibraryInfo;
+import QtCore.QSettings;
+import QtCore.QTextStream;
+import QtCore.QTranslator;
+
+import QtGui.QDesktopServices;
+import QtGui.QFileOpenEvent;
+import QtGui.QMessageBox;
+
+import QtNetwork.QLocalServer;
+import QtNetwork.QLocalSocket;
+import QtNetwork.QNetworkProxy;
+import QtNetwork.QSslSocket;
+
+import QtWebKit.QWebSettings;
+
+import QtCore.QDebug;
+
+DownloadManager *BrowserApplication::s_downloadManager = 0;
+HistoryManager *BrowserApplication::s_historyManager = 0;
+NetworkAccessManager *BrowserApplication::s_networkAccessManager = 0;
+BookmarksManager *BrowserApplication::s_bookmarksManager = 0;
+
+
+/*
+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:
+this(char[] args)
+{
+	super(args);
+	m_localServer = 0;
+    QCoreApplication::setOrganizationName(QLatin1String("Trolltech"));
+    QCoreApplication::setApplicationName(QLatin1String("demobrowser"));
+    QCoreApplication::setApplicationVersion(QLatin1String("0.1"));
+version(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();
+}
+    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;
+    }
+
+version(Q_WS_MAC) {
+    QApplication::setQuitOnLastWindowClosed(false);
+} else {
+    QApplication::setQuitOnLastWindowClosed(true);
+}
+
+    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);
+        }
+    }
+
+#ifndef QT_NO_OPENSSL
+    if (!QSslSocket::supportsSsl()) {
+    QMessageBox::information(0, "Demo Browser",
+                 "This system does not support OpenSSL. SSL websites will not be available.");
+    }
+}
+
+    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();
+
+version(Q_WS_MAC) {
+    connect(this, SIGNAL(lastWindowClosed()),
+            this, SLOT(lastWindowClosed()));
+}
+
+    QTimer::singleShot(0, this, SLOT(postLaunch()));
+}
+
+    ~this()
+{
+    delete s_downloadManager;
+    for (int i = 0; i < m_mainWindows.size(); ++i) {
+        BrowserMainWindow *window = m_mainWindows.at(i);
+        delete window;
+    }
+    delete s_networkAccessManager;
+    delete s_bookmarksManager;
+}
+
+    static BrowserApplication *instance()
+{
+    return (static_cast<BrowserApplication *>(QCoreApplication::instance()));
+}
+
+
+
+    void 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();
+}
+
+    bool isTheOnlyBrowser() const
+{
+    return (m_localServer != 0);
+}
+
+    BrowserMainWindow *mainWindow()
+{
+    clean();
+    if (m_mainWindows.isEmpty())
+        newMainWindow();
+    return m_mainWindows[0];
+}
+
+    QList<BrowserMainWindow*> mainWindows()
+{
+    clean();
+    QList<BrowserMainWindow*> list;
+    for (int i = 0; i < m_mainWindows.count(); ++i)
+        list.append(m_mainWindows.at(i));
+    return list;
+}
+
+
+    QIcon 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);
+}
+
+    void 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 canRestoreSession() const
+{
+    return !m_lastSession.isEmpty();
+}
+
+    static HistoryManager *historyManager()
+{
+    if (!s_historyManager) {
+        s_historyManager = new HistoryManager();
+        QWebHistoryInterface::setDefaultInterface(s_historyManager);
+    }
+    return s_historyManager;
+}
+
+    static CookieJar *cookieJar()
+{
+    return (CookieJar*)networkAccessManager()->cookieJar();
+}
+    static DownloadManager *downloadManager()
+{
+    if (!s_downloadManager) {
+        s_downloadManager = new DownloadManager();
+    }
+    return s_downloadManager;
+}
+
+
+    static NetworkAccessManager *networkAccessManager()
+{
+    if (!s_networkAccessManager) {
+        s_networkAccessManager = new NetworkAccessManager();
+        s_networkAccessManager->setCookieJar(new CookieJar);
+    }
+    return s_networkAccessManager;
+}
+
+
+    static BookmarksManager *bookmarksManager()
+{
+    if (!s_bookmarksManager) {
+        s_bookmarksManager = new BookmarksManager;
+    }
+    return s_bookmarksManager;
+}
+
+
+version(Q_WS_MAC) {
+bool 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);
+}
+}
+
+public slots:
+    BrowserMainWindow *newMainWindow()
+{
+    BrowserMainWindow *browser = new BrowserMainWindow();
+    m_mainWindows.prepend(browser);
+    browser->show();
+    return browser;
+}
+
+    void 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));
+    }
+}
+
+
+version(Q_WS_MAC) {
+import 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);
+}
+}
+
+version(Q_WS_MAC) {
+void lastWindowClosed()
+{
+    clean();
+    BrowserMainWindow *mw = new BrowserMainWindow;
+    mw->slotHome();
+    m_mainWindows.prepend(mw);
+}
+}
+
+
+private slots:
+
+/*!
+    Any actions that can be delayed until the window is visible
+ */
+void postLaunch()
+{
+    QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+    if (directory.isEmpty())
+        directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName();
+    QWebSettings::setIconDatabasePath(directory);
+
+    setWindowIcon(QIcon(QLatin1String(":browser.svg")));
+
+    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 openUrl(const QUrl &url)
+{
+    mainWindow()->loadPage(url.toString());
+}
+
+    void 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();
+}
+
+private:
+    void 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 installTranslator(const QString &name)
+{
+    QTranslator *translator = new QTranslator(this);
+    translator->load(name, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+    QApplication::installTranslator(translator);
+}
+
+    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;
+};
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/browsermainwindow.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,1073 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtGui.QMainWindow;
+import QtGui.QIcon;
+import QtCore.QUrl;
+
+import browsermainwindow;
+
+import autosaver;
+import bookmarks;
+import browserapplication;
+import chasewidget;
+import downloadmanager;
+import history;
+import settings;
+import tabwidget;
+import toolbarsearch;
+import ui_passworddialog;
+import webview;
+
+import QtCore.QSettings;
+
+import QtGui.QDesktopWidget;
+import QtGui.QFileDialog;
+import QtGui.QPlainTextEdit;
+import QtGui.QPrintDialog;
+import QtGui.QPrintPreviewDialog;
+import QtGui.QPrinter;
+import QtGui.QMenuBar;
+import QtGui.QMessageBox;
+import QtGui.QStatusBar;
+import QtGui.QToolBar;
+import QtGui.QInputDialog;
+
+import QtWebKit.QWebFrame;
+import QtWebKit.QWebHistory;
+
+import QtCore.QDebug;
+
+
+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
+	
+static const qint32 BrowserMainWindowMagic = 0xba;
+	
+public:
+    this(QWidget *parent = null, Qt.WindowFlags flags = 0);
+{
+	
+	super(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);
+version(Q_WS_MAC)
+{
+    layout.addWidget(m_bookmarksToolbar);
+    layout.addWidget(new QWidget); // <- OS X tab widget style bug
+} else {
+    addToolBarBreak();
+    addToolBar(m_bookmarksToolbar);
+}
+    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)));
+version(Q_WS_MAC) {
+    connect(m_tabWidget, SIGNAL(lastTabClosed()),
+            this, SLOT(close()));
+} else {
+    connect(m_tabWidget, SIGNAL(lastTabClosed()),
+            m_tabWidget, SLOT(newTab()));
+}
+
+    slotUpdateWindowTitle();
+    loadDefaultState();
+    m_tabWidget.newTab();
+
+    int size = m_tabWidget.lineEditStack().sizeHint().height();
+    m_navigationBar.setIconSize(QSize(size, size));
+
+}
+
+
+
+   ~this()
+{
+    m_autoSaver.changeOccurred();
+    m_autoSaver.saveIfNeccessary();
+}
+
+
+QSize sizeHint()
+{
+    QRect desktopRect = QApplication::desktop().screenGeometry();
+    QSize size = desktopRect.size() * qreal(0.9);
+    return size;
+}
+
+public:
+static QUrl 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 = QUrl::fromEncoded(urlStr.toUtf8(), 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();
+            QByteArray schema = (prefix == QLatin1String("ftp")) ? prefix.toLatin1() : "http";
+            QUrl url =
+                QUrl::fromEncoded(schema + "://" + urlStr.toUtf8(), QUrl::TolerantMode);
+            if (url.isValid())
+                return url;
+        }
+    }
+
+    // Fall back to QUrl's own tolerant parser.
+    QUrl url = QUrl::fromEncoded(string.toUtf8(), QUrl::TolerantMode);
+
+    // finally for cases where the user just types in a hostname add http
+    if (url.scheme().isEmpty())
+        url = QUrl::fromEncoded("http://" + string.toUtf8(), QUrl::TolerantMode);
+    return url;
+}
+
+
+
+TabWidget* tabWidget()
+{
+    return m_tabWidget;
+}
+
+WebView* currentTab()
+{
+    return m_tabWidget.currentWebView();
+}
+
+QByteArray saveState(bool withTabs)
+{
+    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 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;
+}
+
+
+public slots:
+void loadPage(const QString &page)
+{
+    QUrl url = guessUrlFromString(page);
+    loadUrl(url);
+}
+
+
+void slotHome()
+{
+    QSettings settings;
+    settings.beginGroup(QLatin1String("MainWindow"));
+    QString home = settings.value(QLatin1String("home"), QLatin1String("http://qtsoftware.com/")).toString();
+    loadPage(home);
+}
+
+protected:
+    void 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();
+}
+
+private slots:
+void save()
+{
+    BrowserApplication::instance().saveSession();
+
+    QSettings settings;
+    settings.beginGroup(QLatin1String("BrowserMainWindow"));
+    QByteArray data = saveState(false);
+    settings.setValue(QLatin1String("defaultState"), data);
+    settings.endGroup();
+}
+
+void 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 slotUpdateStatusbar(const QString &string);
+{
+    statusBar().showMessage(string, 2000);
+}
+
+
+    void slotUpdateWindowTitle(const QString &title = QString())
+{
+    if (title.isEmpty()) {
+        setWindowTitle(tr("Qt Demo Browser"));
+    } else {
+version(Q_WS_MAC)
+{
+        setWindowTitle(title);
+} else {
+        setWindowTitle(tr("%1 - Qt Demo Browser", "Page title and Browser name").arg(title));
+}
+    }
+}
+
+    void loadUrl(const QUrl &url);
+{
+    if (!currentTab() || !url.isValid())
+        return;
+
+    m_tabWidget.currentLineEdit().setText(QString::fromUtf8(url.toEncoded()));
+    m_tabWidget.loadUrlInCurrentTab(url);
+}
+
+    void slotPreferences()
+{
+    SettingsDialog *s = new SettingsDialog(this);
+    s.show();
+}
+
+void slotFileNew()
+{
+    BrowserApplication::instance().newMainWindow();
+    BrowserMainWindow *mw = BrowserApplication::instance().mainWindow();
+    mw.slotHome();
+}
+
+void 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 slotFilePrintPreview();
+{
+version(QT_NO_PRINTER)
+	{
+    if (!currentTab())
+        return;
+    QPrintPreviewDialog *dialog = new QPrintPreviewDialog(this);
+    connect(dialog, SIGNAL(paintRequested(QPrinter *)),
+            currentTab(), SLOT(print(QPrinter *)));
+    dialog.exec();
+}
+}
+
+
+    void slotFilePrint()
+{
+    if (!currentTab())
+        return;
+    printRequested(currentTab().page().mainFrame());
+}
+
+    void 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 slotFileSaveAs()
+{
+    BrowserApplication::downloadManager().download(currentTab().url(), true);
+}
+
+    void 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 slotEditFindNext()
+{
+    if (!currentTab() && !m_lastSearch.isEmpty())
+        return;
+    currentTab().findText(m_lastSearch);
+}
+
+void slotEditFindPrevious()
+{
+    if (!currentTab() && !m_lastSearch.isEmpty())
+        return;
+    currentTab().findText(m_lastSearch, QWebPage::FindBackward);
+}
+
+
+    void slotShowBookmarksDialog();
+{
+    BookmarksDialog *dialog = new BookmarksDialog(this);
+    connect(dialog, SIGNAL(openUrl(const QUrl&)),
+            m_tabWidget, SLOT(loadUrlInCurrentTab(const QUrl&)));
+    dialog.show();
+}
+
+void slotAddBookmark()
+{
+    WebView *webView = currentTab();
+    QString url = webView.url().toString();
+    QString title = webView.title();
+    AddBookmarkDialog dialog(url, title);
+    dialog.exec();
+}
+
+void slotViewZoomIn()
+{
+    if (!currentTab())
+        return;
+    currentTab().setZoomFactor(currentTab().zoomFactor() + 0.1);
+}
+
+void BslotViewZoomOut()
+{
+    if (!currentTab())
+        return;
+    currentTab().setZoomFactor(currentTab().zoomFactor() - 0.1);
+}
+
+void slotViewResetZoom()
+{
+    if (!currentTab())
+        return;
+    currentTab().setZoomFactor(1.0);
+}
+
+void slotViewZoomTextOnly(bool enable)
+{
+    if (!currentTab())
+        return;
+    currentTab().page().settings().setAttribute(QWebSettings::ZoomTextOnly, enable);
+}
+
+
+    void slotViewToolbar()
+{
+    if (m_navigationBar.isVisible()) {
+        updateToolbarActionText(false);
+        m_navigationBar.close();
+    } else {
+        updateToolbarActionText(true);
+        m_navigationBar.show();
+    }
+    m_autoSaver.changeOccurred();
+}
+
+
+    void slotViewBookmarksBar()
+{
+    if (m_bookmarksToolbar.isVisible()) {
+        updateBookmarksToolbarActionText(false);
+        m_bookmarksToolbar.close();
+    } else {
+        updateBookmarksToolbarActionText(true);
+        m_bookmarksToolbar.show();
+    }
+    m_autoSaver.changeOccurred();
+}
+
+    void slotViewStatusbar()
+{
+    if (statusBar().isVisible()) {
+        updateStatusbarActionText(false);
+        statusBar().close();
+    } else {
+        updateStatusbarActionText(true);
+        statusBar().show();
+    }
+    m_autoSaver.changeOccurred();
+}
+
+void 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 slotViewFullScreen(bool makeFullScreen)
+{
+    if (makeFullScreen) {
+        showFullScreen();
+    } else {
+        if (isMinimized())
+            showMinimized();
+        else if (isMaximized())
+            showMaximized();
+        else showNormal();
+    }
+}
+
+void slotWebSearch()
+{
+    m_toolbarSearch.lineEdit().selectAll();
+    m_toolbarSearch.lineEdit().setFocus();
+}
+
+
+    void 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 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 slotDownloadManager()
+{
+    BrowserApplication::downloadManager().show();
+}
+
+    void slotSelectLineEdit();
+{
+    m_tabWidget.currentLineEdit().selectAll();
+    m_tabWidget.currentLineEdit().setFocus();
+}
+
+    void 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 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 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 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 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 slotSwapFocus()
+{
+    if (currentTab().hasFocus())
+        m_tabWidget.currentLineEdit().setFocus();
+    else
+        currentTab().setFocus();
+}
+
+void printRequested(QWebFrame *frame)
+{
+	version(QT_NO_PRINTER)
+	{
+		QPrinter printer;
+		QPrintDialog *dialog = new QPrintDialog(&printer, this);
+		dialog.setWindowTitle(tr("Print Document"));
+		if (dialog.exec() != QDialog::Accepted)
+			return;
+		frame.print(&printer);
+	}
+}
+
+    void geometryChangeRequested(const QRect &geometry)
+{
+    setGeometry(geometry);
+}
+
+
+    void updateToolbarActionText(bool visible)
+{
+    m_viewToolbar.setText(!visible ? tr("Show Toolbar") : tr("Hide Toolbar"));
+}
+    
+    void updateBookmarksToolbarActionText(bool visible)
+{
+    m_viewBookmarkBar.setText(!visible ? tr("Show Bookmarks bar") : tr("Hide Bookmarks bar"));
+}
+
+
+private:
+void loadDefaultState()
+{
+    QSettings settings;
+    settings.beginGroup(QLatin1String("BrowserMainWindow"));
+    QByteArray data = settings.value(QLatin1String("defaultState")).toByteArray();
+    restoreState(data);
+    settings.endGroup();
+}
+
+
+    void 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();
+
+version(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));
+}
+
+    // 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("Zoom &In"), this, SLOT(slotViewZoomIn()), QKeySequence(Qt.CTRL | Qt.Key_Plus));
+    viewMenu.addAction(tr("Zoom &Out"), this, SLOT(slotViewZoomOut()), QKeySequence(Qt.CTRL | Qt.Key_Minus));
+    viewMenu.addAction(tr("Reset &Zoom"), this, SLOT(slotViewResetZoom()), QKeySequence(Qt.CTRL | Qt.Key_0));
+    QAction *zoomTextOnlyAction = viewMenu.addAction(tr("Zoom &Text Only"));
+    connect(zoomTextOnlyAction, SIGNAL(toggled(bool)), this, SLOT(slotViewZoomTextOnly(bool)));
+    zoomTextOnlyAction.setCheckable(true);
+    zoomTextOnlyAction.setChecked(false);
+
+    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 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 updateStatusbarActionText(bool visible)
+{
+    m_viewStatusbar.setText(!visible ? tr("Show Status Bar") : tr("Hide Status Bar"));
+}
+
+
+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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/chasewidget.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+module chasewidget;
+
+
+import QtGui.QWidget;
+
+import QtCore.QSize;
+import QtGui.QColor;
+import QtGui.QPixmap;
+
+import QtCore.QPoint;
+
+import QtGui.QApplication;
+import QtGui.QHideEvent;
+import QtGui.QPainter;
+import QtGui.QPaintEvent;
+import QtGui.QShowEvent;
+
+/*
+QT_BEGIN_NAMESPACE
+class QHideEvent;
+class QShowEvent;
+class QPaintEvent;
+class QTimerEvent;
+QT_END_NAMESPACE
+*/
+
+class ChaseWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    this(QWidget *parent = 0, QPixmap pixmap = QPixmap(), bool pixmapEnabled = false)
+{
+	super(parent);
+	m_segment = 0;
+     m_delay = 100;
+     m_step = 40;
+     m_timerId = -1;
+	 m_animated = false;
+ m_pixmap = pixmap;
+     m_pixmapEnabled = pixmapEnabled;
+}
+
+    void 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 setPixmapEnabled(bool enable);
+{
+    m_pixmapEnabled = enable;
+}
+
+
+    QSize sizeHint()
+{
+    return QSize(32, 32);
+}
+
+protected:
+    void paintEvent(QPaintEvent *event)
+{
+    Q_UNUSED(event);
+    QPainter p(this);
+    if (m_pixmapEnabled && !m_pixmap.isNull()) {
+        p.drawPixmap(0, 0, m_pixmap);
+        return;
+    }
+
+    int extent = qMin(width() - 8, height() - 8);
+    int displ = extent / 4;
+    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));
+    }
+}
+
+    void timerEvent(QTimerEvent *event)
+{
+    if (event->timerId() == m_timerId) {
+        ++m_segment;
+        update();
+    }
+    QWidget.timerEvent(event);
+}
+
+private:
+    int segmentCount()
+{
+    return 360 / m_step;
+}
+
+
+QColor colorForSegment(int seg)
+{
+    int index = ((seg + m_segment) % segmentCount());
+    int comp = qMax(0, 255 - (index * (255 / segmentCount())));
+    return QColor(comp, comp, comp, 255);
+}
+
+    int m_segment;
+    int m_delay;
+    int m_step;
+    int m_timerId;
+    bool m_animated;
+    QPixmap m_pixmap;
+    bool m_pixmapEnabled;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/cookiejar.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,849 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module cookiejar;
+
+import QtNetwork.QNetworkCookieJar;
+
+import QtCore.QAbstractItemModel;
+import QtCore.QStringList;
+
+import QtGui.QDialog;
+import QtGui.QTableView;
+
+
+#include "cookiejar.h"
+
+#include "autosaver.h"
+
+import QtCore.QDateTime;
+import QtCore.QDir;
+import QtCore.QFile;
+import QtCore.QMetaEnum;
+import QtCore.QSettings;
+import QtCore.QUrl;
+
+import QtGui.QCompleter;
+import QtGui.QDesktopServices;
+import QtGui.QFont;
+import QtGui.QFontMetrics;
+import QtGui.QHeaderView;
+import QtGui.QKeyEvent;
+import QtGui.QSortFilterProxyModel;
+
+import QtWebKit.QWebSettings;
+
+import QtCore.QDebug;
+
+/*
+QT_BEGIN_NAMESPACE
+class QSortFilterProxyModel;
+class QKeyEvent;
+QT_END_NAMESPACE
+
+class AutoSaver;
+*/
+
+static const unsigned int JAR_VERSION = 23;
+
+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;
+}
+
+
+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
+	};
+
+	this(QObject *parent = null)
+	{
+		super(parent);
+		m_loaded = false;
+		m_saveTimer = new AutoSaver(this);
+		m_acceptCookies = AcceptOnlyFromSitesNavigatedTo;
+	}
+    
+	~this()
+	{
+		if (m_keepCookies == KeepUntilExit)
+			clear();
+		m_saveTimer.saveIfNeccessary();
+	}
+
+    QList<QNetworkCookie> cookiesForUrl(const QUrl &url)
+{
+    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 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;
+}
+
+	AcceptPolicy acceptPolicy()
+	{
+		if (!m_loaded)
+			(const_cast<CookieJar*>(this)).load();
+		return m_acceptCookies;
+	}
+    
+	void setAcceptPolicy(AcceptPolicy policy)
+	{
+		if (!m_loaded)
+			load();
+		if (policy == m_acceptCookies)
+			return;
+		m_acceptCookies = policy;
+		m_saveTimer.changeOccurred();
+	}
+
+	KeepPolicy keepPolicy()
+	{
+		if (!m_loaded)
+			(const_cast<CookieJar*>(this)).load();
+		return m_keepCookies;
+	}
+
+	void setKeepPolicy(KeepPolicy policy)
+	{
+		if (!m_loaded)
+			load();
+		if (policy == m_keepCookies)
+			return;
+		m_keepCookies = policy;
+		m_saveTimer.changeOccurred();
+	}
+
+
+	QStringList blockedCookies()
+	{
+		if (!m_loaded)
+			(const_cast<CookieJar*>(this)).load();
+		return m_exceptions_block;
+	}
+
+	QStringList allowedCookies()
+	{
+		if (!m_loaded)
+			(const_cast<CookieJar*>(this)).load();
+		return m_exceptions_allow;
+	}
+    
+	QStringList allowForSessionCookies()
+	{
+		if (!m_loaded)
+			(const_cast<CookieJar*>(this)).load();
+		return m_exceptions_allowForSession;
+	}
+
+	void 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 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 setAllowForSessionCookies(const QStringList &list)
+	{
+		if (!m_loaded)
+			load();
+		m_exceptions_allowForSession = list;
+		qSort(m_exceptions_allowForSession.begin(), m_exceptions_allowForSession.end());
+		m_saveTimer.changeOccurred();
+	}
+
+public slots:
+
+	void clear()
+	{
+		setAllCookies(QList<QNetworkCookie>());
+		m_saveTimer.changeOccurred();
+		emit cookiesChanged();
+	}
+
+	void 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();
+	}
+
+private slots:
+void 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)));
+}
+
+private:
+    void 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();
+}
+
+	void 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();
+	}
+
+	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:
+	this(CookieJar *jar, QObject *parent = null	)
+	{
+		super(parent);
+		m_cookieJar = cookieJar;
+		connect(m_cookieJar, SIGNAL(cookiesChanged()), this, SLOT(cookiesChanged()));
+		m_cookieJar.load();
+	}
+
+QVariant headerData(int section, Qt.Orientation orientation, int role)
+{
+    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 data(const QModelIndex &index, int role = Qt.DisplayRole)
+	{
+		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 columnCount(const QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid()) ? 0 : 6;
+	}
+
+
+	int rowCount(const QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid() || !m_cookieJar) ? 0 : m_cookieJar.allCookies().count();
+	}
+
+
+	bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
+	{
+		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;
+	}
+
+private slots:
+	
+	void cookiesChanged()
+	{
+		reset();
+	}
+
+private:
+	CookieJar *m_cookieJar;
+}
+
+#include "ui_cookies.h"
+#include "ui_cookiesexceptions.h"
+
+class CookiesDialog : public QDialog, public Ui_CookiesDialog
+{
+    Q_OBJECT
+
+public:
+
+	this(CookieJar *cookieJar, QWidget *parent = this) : 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);
+	}
+	
+private:
+
+	QSortFilterProxyModel *m_proxyModel;
+}
+
+class CookieExceptionsModel : public QAbstractTableModel
+{
+    Q_OBJECT
+    friend class CookiesExceptionsDialog;
+
+public:
+	zhis(CookieJar *cookieJar, QObject *parent = null)
+	{
+		super(parent);
+		m_cookieJar = cookiejar;
+		m_allowedCookies = m_cookieJar.allowedCookies();
+		m_blockedCookies = m_cookieJar.blockedCookies();
+		m_sessionCookies = m_cookieJar.allowForSessionCookies();
+	}
+
+	QVariant headerData(int section, Qt.Orientation orientation, int role)
+	{
+		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 data(const QModelIndex &index, int role = Qt.DisplayRole)
+	{
+		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 columnCount(const QModelIndex &parent = QModelIndex());
+	{
+		return (parent.isValid()) ? 0 : 2;
+	}
+	
+	int rowCount(const QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid() || !m_cookieJar) ? 0 : m_allowedCookies.count() + m_blockedCookies.count() + m_sessionCookies.count();
+	}
+
+	bool 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;
+	}
+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:
+	this(CookieJar *cookieJar, QWidget *parent = null)
+	: 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);
+		}
+	}
+
+private slots:
+	void block()
+	{
+		if (domainLineEdit.text().isEmpty())
+			return;
+		m_exceptionsModel.m_blockedCookies.append(domainLineEdit.text());
+		m_cookieJar.setBlockedCookies(m_exceptionsModel.m_blockedCookies);
+		m_exceptionsModel.reset();
+	}
+
+	void allow()
+	{
+		if (domainLineEdit.text().isEmpty())
+			return;
+		m_exceptionsModel.m_allowedCookies.append(domainLineEdit.text());
+		m_cookieJar.setAllowedCookies(m_exceptionsModel.m_allowedCookies);
+		m_exceptionsModel.reset();
+	}
+	void allowForSession()
+	{
+		if (domainLineEdit.text().isEmpty())
+			return;
+		m_exceptionsModel.m_sessionCookies.append(domainLineEdit.text());
+		m_cookieJar.setAllowForSessionCookies(m_exceptionsModel.m_sessionCookies);
+		m_exceptionsModel.reset();
+	}
+	
+	void textChanged(const QString &text)
+	{
+		bool enabled = !text.isEmpty();
+		blockButton.setEnabled(enabled);
+		allowButton.setEnabled(enabled);
+		allowForSessionButton.setEnabled(enabled);
+	}
+	
+private:
+	
+	CookieExceptionsModel *m_exceptionsModel;
+	QSortFilterProxyModel *m_proxyModel;
+	CookieJar *m_cookieJar;
+}
Binary file demos/browser/data/addtab.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/data/browser.svg	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,411 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="48px"
+   height="48px"
+   id="svg2160"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   inkscape:export-filename="c:\icons\qtbrowser48.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   sodipodi:docbase="C:\icons"
+   sodipodi:docname="browser.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs2162"><linearGradient
+   id="linearGradient3808">
+  <stop
+     id="stop3810"
+     offset="0"
+     style="stop-color:#000000;stop-opacity:0.54263568;" />
+  <stop
+     id="stop3812"
+     offset="1"
+     style="stop-color:#000000;stop-opacity:0;" />
+</linearGradient>
+<inkscape:perspective
+   sodipodi:type="inkscape:persp3d"
+   inkscape:vp_x="0 : 24 : 1"
+   inkscape:vp_y="0 : 1000 : 0"
+   inkscape:vp_z="48 : 24 : 1"
+   inkscape:persp3d-origin="24 : 16 : 1"
+   id="perspective63" />
+<linearGradient
+   id="linearGradient3326">
+  <stop
+     style="stop-color:#000000;stop-opacity:0.3137255;"
+     offset="0"
+     id="stop3328" />
+  <stop
+     style="stop-color:#000000;stop-opacity:0;"
+     offset="1"
+     id="stop3330" />
+</linearGradient>
+<linearGradient
+   id="linearGradient3318">
+  <stop
+     style="stop-color:#000000;stop-opacity:0.3137255;"
+     offset="0"
+     id="stop3320" />
+  <stop
+     style="stop-color:#000000;stop-opacity:0;"
+     offset="1"
+     id="stop3322" />
+</linearGradient>
+<linearGradient
+   id="linearGradient3302">
+  <stop
+     style="stop-color:#000000;stop-opacity:0.3137255;"
+     offset="0"
+     id="stop3304" />
+  <stop
+     style="stop-color:#000000;stop-opacity:0;"
+     offset="1"
+     id="stop3306" />
+</linearGradient>
+<linearGradient
+   id="linearGradient3267">
+  <stop
+     style="stop-color:#000000;stop-opacity:1;"
+     offset="0"
+     id="stop3269" />
+  <stop
+     id="stop3275"
+     offset="0.79661018"
+     style="stop-color:#000000;stop-opacity:1;" />
+  <stop
+     style="stop-color:#000000;stop-opacity:0;"
+     offset="1"
+     id="stop3271" />
+</linearGradient>
+<linearGradient
+   id="linearGradient3745">
+  <stop
+     style="stop-color:#ffffff;stop-opacity:0.19587629;"
+     offset="0"
+     id="stop3747" />
+  <stop
+     style="stop-color:#7cb2ff;stop-opacity:0.07216495;"
+     offset="1"
+     id="stop3749" />
+</linearGradient>
+<linearGradient
+   inkscape:collect="always"
+   id="linearGradient3561">
+  <stop
+     style="stop-color:#b1d0ff;stop-opacity:1;"
+     offset="0"
+     id="stop3563" />
+  <stop
+     style="stop-color:#b1d0ff;stop-opacity:0;"
+     offset="1"
+     id="stop3565" />
+</linearGradient>
+<linearGradient
+   id="linearGradient3181">
+  <stop
+     style="stop-color:#4f7a33;stop-opacity:1;"
+     offset="0"
+     id="stop3183" />
+  <stop
+     style="stop-color:#204712;stop-opacity:1;"
+     offset="1"
+     id="stop3185" />
+</linearGradient>
+<linearGradient
+   id="linearGradient3143">
+  <stop
+     style="stop-color:#c1dbff;stop-opacity:1;"
+     offset="0"
+     id="stop3145" />
+  <stop
+     style="stop-color:#004e92;stop-opacity:1;"
+     offset="1"
+     id="stop3147" />
+</linearGradient>
+<radialGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3143"
+   id="radialGradient3149"
+   cx="9.1428566"
+   cy="15.142858"
+   fx="9.1428566"
+   fy="15.142858"
+   r="20.121096"
+   gradientUnits="userSpaceOnUse" />
+<radialGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3181"
+   id="radialGradient3187"
+   cx="10.739879"
+   cy="18.250999"
+   fx="10.739879"
+   fy="18.250999"
+   r="7.4191086"
+   gradientTransform="matrix(1.0504709,0,0,1.5077925,-0.3797113,-9.2677171)"
+   gradientUnits="userSpaceOnUse" />
+<radialGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3181"
+   id="radialGradient3195"
+   cx="14.947268"
+   cy="35.920116"
+   fx="14.947268"
+   fy="35.920116"
+   r="6.0472684"
+   gradientTransform="matrix(1,0,0,0.7248478,0,9.8834985)"
+   gradientUnits="userSpaceOnUse" />
+<radialGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3181"
+   id="radialGradient3203"
+   cx="34.227203"
+   cy="24.681196"
+   fx="34.227203"
+   fy="24.681196"
+   r="6.7517419"
+   gradientTransform="matrix(0.9941509,-0.1079997,0.2962199,2.7267411,-7.1108629,-38.921508)"
+   gradientUnits="userSpaceOnUse" />
+<radialGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3561"
+   id="radialGradient3567"
+   cx="22.714285"
+   cy="23.571428"
+   fx="22.714285"
+   fy="23.571428"
+   r="19.828572"
+   gradientUnits="userSpaceOnUse" />
+<linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3745"
+   id="linearGradient3751"
+   x1="0.84126461"
+   y1="13.678415"
+   x2="31.397495"
+   y2="13.678415"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0.8791332,0.7829527,-0.6285195,1.0951445,14.147627,-10.49311)" />
+<filter
+   inkscape:collect="always"
+   id="filter4176">
+  <feGaussianBlur
+     inkscape:collect="always"
+     stdDeviation="0.27747502"
+     id="feGaussianBlur4178" />
+</filter>
+<radialGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3267"
+   id="radialGradient3273"
+   cx="22.714285"
+   cy="23.571428"
+   fx="22.714285"
+   fy="23.571428"
+   r="19.428572"
+   gradientUnits="userSpaceOnUse" />
+<inkscape:perspective
+   id="perspective136"
+   inkscape:persp3d-origin="138.6795 : 92.479329 : 1"
+   inkscape:vp_z="277.35901 : 138.71899 : 1"
+   inkscape:vp_y="0 : 1000 : 0"
+   inkscape:vp_x="0 : 138.71899 : 1"
+   sodipodi:type="inkscape:persp3d" />
+
+	
+	
+	
+	
+	
+	
+	
+	
+	
+<linearGradient
+   inkscape:collect="always"
+   xlink:href="#linearGradient3808"
+   id="linearGradient3806"
+   x1="32.829472"
+   y1="32.055603"
+   x2="34.522324"
+   y2="-1.0290829"
+   gradientUnits="userSpaceOnUse"
+   gradientTransform="matrix(0.8832227,0,0,1,-8.0103007,9.1923882)" />
+</defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="5.6568542"
+     inkscape:cx="30.924085"
+     inkscape:cy="24.59691"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1299"
+     inkscape:window-height="883"
+     inkscape:window-x="373"
+     inkscape:window-y="89"
+     showguides="false" />
+  <metadata
+     id="metadata2165">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Qt Browser</dc:title>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Jens Bache-Wiig</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title>Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).</dc:title>
+          </cc:Agent>
+        </dc:rights>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.78108437;fill:url(#radialGradient3273);fill-opacity:1;stroke:none;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3407"
+       sodipodi:cx="22.714285"
+       sodipodi:cy="23.571428"
+       sodipodi:rx="19.428572"
+       sodipodi:ry="19.428572"
+       d="M 42.142857,23.571428 A 19.428572,19.428572 0 1 1 3.2857132,23.571428 A 19.428572,19.428572 0 1 1 42.142857,23.571428 z"
+       transform="matrix(1.0818892,0,0,1.0409446,-2.4313375,0.4303723)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:url(#radialGradient3149);fill-opacity:1;stroke:none;stroke-width:0.80000000000000004;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2170"
+       sodipodi:cx="22.714285"
+       sodipodi:cy="23.571428"
+       sodipodi:rx="19.428572"
+       sodipodi:ry="19.428572"
+       d="M 42.142857 23.571428 A 19.428572 19.428572 0 1 1  3.2857132,23.571428 A 19.428572 19.428572 0 1 1  42.142857 23.571428 z" />
+    <path
+       d="M 26.602136,8.2160843 C 26.322653,8.1637524 26.048884,8.1512446 25.78375,8.1745351 L 25.783243,8.1743913 C 25.783243,8.1743913 23.973525,8.3138471 23.891496,8.3211793 C 22.239361,8.4705552 20.985434,10.008307 20.985434,12.131916 L 20.985434,37.174579 L 22.83515,39.126673 L 41.425135,33.998394 C 42.704203,33.746799 43.714709,33.629384 43.714709,31.78483 L 43.714709,11.392226 L 26.602136,8.2160843 z"
+       id="path2998"
+       style="fill:url(#linearGradient3806);fill-opacity:1"
+       sodipodi:nodetypes="cccsccccccc" />
+    <path
+       style="fill:url(#radialGradient3203);fill-opacity:1;fill-rule:evenodd;stroke:#1d3215;stroke-width:0.51392877000000003;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 37.535517,11.721122 C 32.782916,8.7478602 30.602351,6.3542385 32.09957,13.4346 C 32.320572,14.27055 33.291276,13.739232 33.291276,14.862228 C 33.291276,16.155819 32.607502,17.380765 31.797574,18.146663 C 30.959323,18.939344 31.011357,20.258984 31.797574,21.002459 C 33.06234,22.198469 33.942515,22.715936 35.572536,22.715936 C 36.6448,22.715936 37.003629,23.274262 37.23352,24.143834 C 37.362263,24.630808 38.410486,25.085663 38.894503,25.428942 C 38.938905,25.460433 38.139512,26.551348 38.139512,27.999158 C 38.139512,29.113512 38.405167,29.358325 38.743505,29.998215 C 38.949111,30.387072 36.418877,30.283794 36.025532,30.283794 C 35.005751,30.283794 34.181701,30.712163 33.15656,30.712163 C 32.264543,30.712163 31.099578,30.3566 31.344578,31.283323 C 31.763542,32.868074 32.552566,33.932342 32.552566,35.709806 C 32.552566,36.862272 31.047367,37.598377 30.287588,38.137232 C 29.30273,38.835721 29.133207,39.307154 28.475606,40.136289 C 28.132145,40.569341 26.990548,41.409612 28.475606,40.707448 C 29.476144,40.234375 31.192063,39.423774 32.09957,38.565601 C 33.257846,37.470293 34.527421,37.269266 35.723534,36.138176 C 36.659137,35.253436 37.512933,34.691155 38.29051,33.710749 C 39.024031,32.785889 39.498498,31.90347 39.498498,30.712163 C 39.498498,29.682482 39.308098,28.750366 39.951493,28.141948 C 40.902684,24.235856 42.225874,19.789742 39.751646,16.005086 C 38.569376,15.014407 37.717516,13.109859 37.535517,11.721122 z "
+       id="path3151"
+       sodipodi:nodetypes="ccsssssssssssssssssssccc" />
+    <path
+       style="fill:url(#radialGradient3187);fill-opacity:1;fill-rule:evenodd;stroke:#063a0a;stroke-width:0.51231807;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 14.777083,7.8630009 C 14.047432,8.4403746 12.751987,10.898939 13.27641,12.146301 C 13.709874,13.177316 14.920827,13.613143 15.827553,13.859622 C 16.568703,14.061091 17.049015,14.457271 17.478293,15.001835 C 17.832696,15.451415 17.971105,16.346745 18.078563,16.857932 C 18.298637,17.904845 18.947911,17.058563 17.62836,18.000145 C 17.234352,18.281296 14.875696,18.000145 14.476948,18.000145 C 11.976825,18.384083 14.297504,19.464893 14.92715,20.712903 C 15.204987,21.770261 15.377352,22.405336 15.377352,23.711213 C 15.377352,24.875672 15.377352,24.78389 15.377352,25.99564 C 15.377352,27.194757 15.044241,27.28063 13.876679,27.28063 C 13.023055,27.28063 12.647321,26.423969 11.625669,26.423969 C 10.400599,26.423969 11.303539,27.667106 11.475602,27.994513 C 12.006402,29.004538 11.662121,29.599737 10.875334,28.851174 C 9.855722,27.881096 8.8280305,26.760556 8.0240557,25.99564 C 2.8789379,25.807372 4.5677903,23.466499 3.9722395,18.999582 C 5.041259,16.526382 4.7558935,17.248897 7.2737194,12.574632 C 10.149914,9.5491592 13.589212,5.9532919 14.777083,7.8630009 z"
+       id="path3159"
+       sodipodi:nodetypes="csssssccsssssscccc" />
+    <path
+       style="fill:url(#radialGradient3195);fill-opacity:1;fill-rule:evenodd;stroke:#163c0c;stroke-width:0.59999999999999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 10.265966,34.571429 C 9.245427,35.081699 8.6225774,36.042538 9.980252,36.857143 C 10.637564,37.25153 11.478587,37.606311 12.265966,38 C 13.258976,38.496505 14.481138,39.018522 15.408823,39.714286 C 16.227572,40.328348 15.587589,39.928184 16.123109,38.857143 C 16.827927,37.447507 18.14516,38.79674 18.837395,39.142857 C 20.044787,39.746554 20.46001,38.652394 20.694537,37.714286 C 20.459863,35.791335 18.579948,34.625723 17.123109,33.285715 C 16.704922,32.588736 15.507117,31.689713 14.837395,31.857143 C 13.49505,33.304042 12.350312,33.960279 10.265966,34.571429 z "
+       id="path3161"
+       sodipodi:nodetypes="cssssscccc" />
+    <path
+       sodipodi:type="arc"
+       style="fill:none;fill-opacity:1;stroke:url(#radialGradient3567);stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.6502732"
+       id="path3557"
+       sodipodi:cx="22.714285"
+       sodipodi:cy="23.571428"
+       sodipodi:rx="19.428572"
+       sodipodi:ry="19.428572"
+       d="M 42.142857 23.571428 A 19.428572 19.428572 0 1 1  3.2857132,23.571428 A 19.428572 19.428572 0 1 1  42.142857 23.571428 z"
+       transform="matrix(0.95317,0,0,0.95317,0.9922816,1.1752786)" />
+    <path
+       style="fill:url(#linearGradient3751);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 39.916926,27.786316 C 44.588637,26.790847 38.225604,13.201712 32.946381,8.5000566 C 18.135275,-0.40265528 10.844456,5.6490056 3.6645529,16.333771 C 5.7478288,18.189127 14.704728,33.158645 39.916926,27.786316 z"
+       id="path3578"
+       sodipodi:nodetypes="cccs" />
+    <path
+       d="M 45.902562,20.610592 C 46.007701,20.610592 46.120332,20.603354 46.240455,20.590275 L 45.609873,20.590275 C 45.697743,20.603608 45.798946,20.610592 45.902562,20.610592 z"
+       id="path3012"
+       style="fill:#0a6333" />
+    <path
+       sodipodi:type="arc"
+       style="fill:none;fill-opacity:1;stroke:#273e5e;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3818"
+       sodipodi:cx="22.714285"
+       sodipodi:cy="23.571428"
+       sodipodi:rx="19.428572"
+       sodipodi:ry="19.428572"
+       d="M 42.142857,23.571428 A 19.428572,19.428572 0 1 1 3.2857132,23.571428 A 19.428572,19.428572 0 1 1 42.142857,23.571428 z"
+       transform="matrix(0.9754581,0,0,0.9754581,0.3821951,0.7002631)" />
+    <g
+       transform="matrix(0.1269799,0,0,0.1269799,23.283534,9.5774104)"
+       id="g236">
+	<path
+   style="fill:#024c1c"
+   id="path238"
+   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" />
+
+	<path
+   style="fill:#66b036"
+   id="path240"
+   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" />
+
+	<g
+   id="g242">
+		<path
+   style="fill:#ffffff"
+   id="path244"
+   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" />
+
+	</g>
+
+	<polygon
+   style="fill:#0a6333"
+   id="polygon246"
+   points="159.027,83.112 145.722,82.501 145.722,82.785 152.854,83.112 159.027,83.112 " />
+
+	<path
+   style="fill:#024c1c"
+   id="path248"
+   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" />
+
+	<path
+   style="fill:#024c1c"
+   id="path250"
+   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" />
+
+	<path
+   style="fill:#0a6333"
+   id="path252"
+   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" />
+
+	<path
+   style="fill:#ffffff"
+   id="path254"
+   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" />
+
+	<path
+   style="fill:#024c1c"
+   id="path256"
+   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" />
+
+</g>
+  </g>
+</svg>
Binary file demos/browser/data/closetab.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/data/data.qrc	Sun May 17 18:49:59 2009 +0000
@@ -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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/data/defaultbookmarks.xbel	Sun May 17 18:49:59 2009 +0000
@@ -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://qtsoftware.com/">
+            <title>Qt Software</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>Qt 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>
Binary file demos/browser/data/defaulticon.png has changed
Binary file demos/browser/data/history.png has changed
Binary file demos/browser/data/loading.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/downloadmanager.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,663 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module downloadmanager;
+
+import ui_downloads;
+import ui_downloaditem;
+
+import QtNetwork.QNetworkReply;
+
+import QtCore.QFile;
+import QtCore.QTime;
+
+
+import autosaver;
+import browserapplication;
+import networkaccessmanager;
+
+import math;
+
+import QtCore.QMetaEnum;
+import QtCore.QSettings;
+
+import QtGui.QDesktopServices;
+import QtGui.QFileDialog;
+import QtGui.QHeaderView;
+import QtGui.QFileIconProvider;
+
+import QtCore.QDebug;
+
+import QtWebKit.QWebSettings;
+
+
+
+class DownloadItem : public QWidget, public Ui_DownloadItem
+{
+    Q_OBJECT
+
+signals:
+    void statusChanged();
+
+public:
+ 
+/*!
+    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(QNetworkReply *reply = 0, bool requestFileName = false, QWidget *parent = 0)
+    : 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();
+}
+
+
+    bool downloading()
+{
+    return (progressBar.isVisible());
+}
+
+
+    bool downloadedSuccessfully()
+{
+    return (stopButton.isHidden() && tryAgainButton.isHidden());
+}
+
+
+    QUrl m_url;
+
+    QFile m_output;
+    QNetworkReply *m_reply;
+
+private slots:
+    void stop()
+{
+    setUpdatesEnabled(false);
+    stopButton.setEnabled(false);
+    stopButton.hide();
+    tryAgainButton.setEnabled(true);
+    tryAgainButton.show();
+    setUpdatesEnabled(true);
+    m_reply.abort();
+}
+
+    void 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 open()
+{
+    QFileInfo info(m_output);
+    QUrl url = QUrl::fromLocalFile(info.absolutePath());
+    QDesktopServices::openUrl(url);
+}
+
+    void 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 error(QNetworkReply::NetworkError code)
+{
+    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 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 metaDataChanged()
+{
+    qDebug() << "DownloadItem::metaDataChanged: not handled.";
+}
+
+    void finished()
+{
+    progressBar.hide();
+    stopButton.setEnabled(false);
+    stopButton.hide();
+    m_output.close();
+    updateInfoLabel();
+    emit statusChanged();
+}
+
+private:
+    void 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();
+}
+
+    void 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 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 dataString(int size)
+{
+    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);
+}
+
+    QString saveFileName(const QString &directory);
+{
+    // 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;
+}
+
+    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 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.
+  */
+this(QWidget *parent = null)
+    : 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();
+}
+
+    ~this()
+{
+    m_autoSaver.changeOccurred();
+    m_autoSaver.saveIfNeccessary();
+    if (m_iconProvider)
+        delete m_iconProvider;
+}
+
+    int activeDownloads()
+{
+    int count = 0;
+    for (int i = 0; i < m_downloads.count(); ++i) {
+        if (m_downloads.at(i).stopButton.isEnabled())
+            ++count;
+    }
+    return count;
+}
+
+    RemovePolicy removePolicy()
+{
+    return m_removePolicy;
+}
+
+    void setRemovePolicy(RemovePolicy policy)
+{
+    if (policy == m_removePolicy)
+        return;
+    m_removePolicy = policy;
+    m_autoSaver.changeOccurred();
+}
+
+
+public slots:
+    void download(const QNetworkRequest &request, bool requestFileName = false);
+{
+    if (request.url().isEmpty())
+        return;
+    handleUnsupportedContent(m_manager.get(request), requestFileName);
+}
+
+
+    inline void download(const QUrl &url, bool requestFileName = false)
+        { download(QNetworkRequest(url), requestFileName); }
+    void handleUnsupportedContent(QNetworkReply *reply, bool requestFileName = false);
+{
+    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 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();
+}
+
+private slots:
+    void save()
+{
+    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 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);
+}
+
+private:
+    void 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 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)
+{
+}
+
+    void 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);
+}
+
+    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)
+{
+    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 rowCount(const QModelIndex &parent = QModelIndex())
+{
+    return (parent.isValid()) ? 0 : m_downloadManager.m_downloads.count();
+}
+
+
+    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
+{
+    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;
+}
+
+private:
+    DownloadManager *m_downloadManager;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/edittableview.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+module edittableview;
+
+
+import QtGui.QTableView;
+
+
+class EditTableView : public QTableView
+{
+    Q_OBJECT
+
+public:
+    EditTableView(QWidget *parent = null)
+{
+	super(parent);
+}
+
+    void keyPressEvent(QKeyEvent *event)
+{
+    if ((event.key() == Qt::Key_Delete
+        || event.key() == Qt::Key_Backspace)
+        && model()) {
+        removeOne();
+    } else {
+        QAbstractItemView::keyPressEvent(event);
+    }
+}
+
+public slots:
+    void 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 removeAll()
+{
+    if (model())
+        model().removeRows(0, model().rowCount(rootIndex()), rootIndex());
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/edittreeview.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+module edittreeview;
+
+
+import QtGui.QTreeView;
+import QtGui.QKeyEvent;
+
+class EditTreeView : public QTreeView
+{
+    Q_OBJECT
+
+public:
+this(QWidget *parent = null)
+{
+	super(parent);
+}
+    void keyPressEvent(QKeyEvent *event)
+{
+    if ((event.key() == Qt::Key_Delete
+        || event.key() == Qt::Key_Backspace)
+        && model()) {
+        removeOne();
+    } else {
+        QAbstractItemView::keyPressEvent(event);
+    }
+}
+
+public slots:
+    void removeOne()
+{
+    if (!model())
+        return;
+    QModelIndex ci = currentIndex();
+    int row = ci.row();
+    model().removeRow(row, ci.parent());
+}
+
+    void removeAll()
+{
+    if (!model())
+        return;
+    model().removeRows(0, model().rowCount(rootIndex()), rootIndex());
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/history.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,1517 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module history;
+
+#include "autosaver.h"
+#include "browserapplication.h"
+
+import QtCore.QBuffer;
+import QtCore.QDir;
+import QtCore.QFile;
+import QtCore.QFileInfo;
+import QtCore.QSettings;
+import QtCore.QTemporaryFile;
+import QtCore.QTextStream;
+
+import QtCore.QtAlgorithms;
+
+import QtGui.QClipboard;
+import QtGui.QDesktopServices;
+import QtGui.QHeaderView;
+import QtGui.QStyle;
+
+import QtWebKit.QWebHistoryInterface;
+import QtWebKit.QWebSettings;
+
+import QtCore.QDebug;
+
+
+#include "modelmenu.h"
+
+import QtCore.QDateTime;
+import QtCore.QHash;
+import QtCore.QObject;
+import QtCore.QTimer;
+import QtCore.QUrl;
+
+import QtGui.QSortFilterProxyModel;
+
+import QWebHistoryInterface;
+
+static const unsigned int HISTORY_VERSION = 23;
+
+
+class HistoryItem
+{
+public:
+    this() {}
+    this(const QString &u, const QDateTime &d = QDateTime(), const QString &t = QString())
+	    {
+		   title = t;
+		    url = u;
+		    dateTime = d;
+		}
+
+    inline bool operator==(const HistoryItem &other)
+        { return other.title == title
+          && other.url == url && other.dateTime == dateTime;
+	}
+
+    // history is sorted in reverse
+    inline bool operator <(const HistoryItem &other)
+        { 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:
+
+	this(QObject *parent = null)
+	{
+		super(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);
+	}
+
+
+	~this()
+	{
+		m_saveTimer.saveIfNeccessary();
+	}
+
+
+	bool historyContains(const QString &url)
+	{
+		return m_historyFilterModel.historyContains(url);
+	}
+
+	void addHistoryEntry(QString &url)
+	{
+		QUrl cleanUrl(url);
+		cleanUrl.setPassword(QString());
+		cleanUrl.setHost(cleanUrl.host().toLower());
+		HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime());
+		addHistoryItem(item);
+	}
+
+	void updateHistoryItem(QUrl &url, 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 historyLimit()
+	{
+		return m_historyLimit;
+	}
+
+
+	void setHistoryLimit(int limit)
+	{
+		if (m_historyLimit == limit)
+			return;
+		m_historyLimit = limit;
+		checkForExpired();
+		m_saveTimer.changeOccurred();
+	}
+
+
+	QList<HistoryItem> history()
+	{
+		return m_history;
+	}
+
+
+	void setHistory(QList<HistoryItem> &history, bool loadedAndSorted = false);
+	{
+		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();
+	}
+
+
+	// History manager keeps around these models for use by the completer and other classes
+	HistoryModel *historyModel();
+	{
+		return m_historyModel;
+	}
+
+
+	HistoryFilterModel *historyFilterModel()
+	{
+		return m_historyFilterModel;
+	}
+
+
+	HistoryTreeModel *historyTreeModel()
+	{
+		return m_historyTreeModel;
+	}
+
+
+public slots:
+
+	void clear()
+	{
+		m_history.clear();
+		m_lastSavedUrl = QString();
+		m_saveTimer.changeOccurred();
+		m_saveTimer.saveIfNeccessary();
+		historyReset();
+	}
+
+	void loadSettings()
+	{
+		// load settings
+		QSettings settings;
+		settings.beginGroup(QLatin1String("history"));
+		m_historyLimit = settings.value(QLatin1String("historyLimit"), 30).toInt();
+	}
+
+private slots:
+    void 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;
+}
+
+void 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);
+}
+
+protected:
+void addHistoryItem(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();
+}
+
+private:
+	
+void 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();
+    }
+}
+
+    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()
+{
+    reset();
+}
+
+void entryAdded()
+{
+    beginInsertRows(QModelIndex(), 0, 0);
+    endInsertRows();
+}
+
+    void entryUpdated(int offset)
+{
+    QModelIndex idx = index(offset, 0);
+    emit dataChanged(idx, idx);
+}
+
+public:
+
+	enum Roles {
+		DateRole = Qt.UserRole + 1,
+		DateTimeRole = Qt.UserRole + 2,
+		UrlRole = Qt.UserRole + 3,
+		UrlStringRole = Qt.UserRole + 4
+	};
+
+	this(HistoryManager *history, QObject *parent = null)
+	{
+		super(parent);
+		m_history = history;
+
+		Q_ASSERT(m_history);
+		connect(m_history, SIGNAL(historyReset()),
+		this, SLOT(historyReset()));
+		connect(m_history, SIGNAL(entryRemoved(HistoryItem &)),
+		this, SLOT(historyReset()));
+
+		connect(m_history, SIGNAL(entryAdded(HistoryItem &)),
+		this, SLOT(entryAdded()));
+		connect(m_history, SIGNAL(entryUpdated(int)),
+		this, SLOT(entryUpdated(int)));
+	}
+    
+	QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole)
+	{
+		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 data(QModelIndex &index, int role = Qt.DisplayRole)
+	{
+		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 columnCount(QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid()) ? 0 : 2;
+	}
+
+	int rowCount(QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid()) ? 0 : m_history.history().count();
+	}
+
+	bool removeRows(int row, int count, QModelIndex &parent = QModelIndex())
+	{
+		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;
+	}
+
+
+private:
+	HistoryManager *m_history;
+}
+
+const uint MOVEDROWS = 15;
+
+/*!
+    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:
+
+	this(QAbstractItemModel *sourceModel, QObject *parent = null)
+	{
+		super(parent);
+		m_loaded = false;
+		setSourceModel(sourceModel);
+	}
+
+	inline bool historyContains(QString &url)
+	{
+		load();
+		return m_historyHash.contains(url);
+	}
+	
+    int historyLocation(QString &url);
+{
+    load();
+    if (!m_historyHash.contains(url))
+        return 0;
+    return sourceModel().rowCount() - m_historyHash.value(url);
+}
+
+    QModelIndex mapFromSource(QModelIndex &sourceIndex)
+{
+    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 mapToSource(QModelIndex &proxyIndex)
+	{
+		load();
+		int sourceRow = sourceModel().rowCount() - proxyIndex.internalId();
+		return sourceModel().index(sourceRow, proxyIndex.column());
+	}
+
+	void setSourceModel(QAbstractItemModel *newSourceModel)
+	{
+		if (sourceModel()) {
+			disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+			disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex &, QModelIndex &)),
+			this, SLOT(dataChanged(QModelIndex &, QModelIndex &)));
+			disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)),
+			this, SLOT(sourceRowsInserted(QModelIndex &, int, int)));
+			disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+			this, SLOT(sourceRowsRemoved(QModelIndex &, int, int)));
+		}
+
+		QAbstractProxyModel::setSourceModel(newSourceModel);
+
+		if (sourceModel()) {
+			m_loaded = false;
+			connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+			connect(sourceModel(), SIGNAL(dataChanged(QModelIndex &, QModelIndex &)),
+			this, SLOT(sourceDataChanged(QModelIndex &, QModelIndex &)));
+			connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)),
+			this, SLOT(sourceRowsInserted(QModelIndex &, int, int)));
+			connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+			this, SLOT(sourceRowsRemoved(QModelIndex &, int, int)));
+		}
+	}
+
+	QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole);
+	{
+		return sourceModel().headerData(section, orientation, role);
+	}
+
+	int rowCount(QModelIndex &parent = QModelIndex())
+	{
+		load();
+		if (parent.isValid())
+			return 0;
+		return m_historyHash.count();
+	}
+
+	int columnCount(QModelIndex &parent = QModelIndex());
+	{
+		return (parent.isValid()) ? 0 : 2;
+	}
+
+	QModelIndex index(int, int, QModelIndex& = QModelIndex())
+	{
+		load();
+		if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent))
+			return QModelIndex();
+
+		return createIndex(row, column, m_sourceRow[row]);
+	}
+
+
+	QModelIndex parent(QModelIndex& index= QModelIndex())
+	{
+		return QModelIndex();
+	}
+
+/*
+    Removing a continuous block of rows will remove filtered rows too as this is
+    the users intention.
+*/
+	bool removeRows(int row, int count, QModelIndex &parent = QModelIndex());
+	{
+		if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid())
+			return false;
+		int lastRow = row + count - 1;
+		disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+		this, SLOT(sourceRowsRemoved(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(QModelIndex &, int, int)),
+		this, SLOT(sourceRowsRemoved(QModelIndex &, int, int)));
+		m_loaded = false;
+		if (oldCount - count != rowCount())
+			reset();
+		return true;
+	}
+
+
+	QVariant data(QModelIndex &index, int role = Qt.DisplayRole);
+	{
+		return QAbstractProxyModel::data(index, role);
+	}
+
+private slots:
+
+	void sourceReset()
+	{
+		m_loaded = false;
+		reset();
+	}
+
+
+	void sourceDataChanged(QModelIndex &topLeft, QModelIndex &bottomRight)
+	{
+		emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight));
+	}
+
+	void sourceRowsRemoved(QModelIndex &, int start, int end)
+	{
+		Q_UNUSED(start);
+		Q_UNUSED(end);
+		sourceReset();
+	}
+
+
+	void sourceRowsInserted(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();
+	}
+
+private:
+	void load()
+	{
+		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;
+	}
+
+
+	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:
+    
+/*
+    Maps the first bunch of items of the source model to the root
+*/
+HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = null)
+{
+	super(parent);
+	m_treeModel = sourceModel;
+	setSourceModel(sourceModel);
+}
+
+int columnCount(QModelIndex &parent)
+{
+	return m_treeModel.columnCount(mapToSource(parent));
+}
+
+	int rowCount(QModelIndex &parent = QModelIndex());
+	{
+		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 mapFromSource(QModelIndex &sourceIndex)
+	{
+		// currently not used or autotested
+		Q_ASSERT(false);
+		int sr = m_treeModel.mapToSource(sourceIndex).row();
+		return createIndex(sourceIndex.row(), sourceIndex.column(), sr);
+	}
+
+	QModelIndex mapToSource(QModelIndex &proxyIndex)
+	{
+		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 index(int, int, QModelIndex &parent = QModelIndex());
+	{
+		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 parent(QModelIndex &index = QModelIndex());
+	{
+		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);
+	}
+
+	int bumpedRows()
+	{
+		QModelIndex first = m_treeModel.index(0, 0);
+		if (!first.isValid())
+			return 0;
+		return qMin(m_treeModel.rowCount(first), MOVEDROWS);
+	}
+
+private:
+
+	HistoryTreeModel *m_treeModel;
+}
+
+// Menu that is dynamically populated from the history
+class HistoryMenu : public ModelMenu
+{
+    Q_OBJECT
+
+signals:
+	void openUrl(QUrl &url);
+
+public:
+
+	this(QWidget *parent = null)
+	{
+		super(parent);
+		m_history = 0;
+		connect(this, SIGNAL(activated(QModelIndex &)),
+		this, SLOT(activated(QModelIndex &)));
+		setHoverRole(HistoryModel.UrlStringRole);
+	}
+
+void setInitialActions(QList<QAction*> actions)
+{
+	m_initialActions = actions;
+	for (int i = 0; i < m_initialActions.count(); ++i)
+		addAction(m_initialActions.at(i));
+}
+
+protected:
+
+	bool 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 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);
+	}
+
+private slots:
+	void activated(QModelIndex &index)
+	{
+		emit openUrl(index.data(HistoryModel.UrlRole).toUrl());
+	}
+
+	void showHistoryDialog()
+	{
+		HistoryDialog *dialog = new HistoryDialog(this);
+		connect(dialog, SIGNAL(openUrl(QUrl&)),
+		    this, SIGNAL(openUrl(QUrl&)));
+		dialog.show();
+	}
+
+
+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:
+	this(QObject *parent = null)
+	{
+		super(parent);
+	}
+
+	QVariant data(QModelIndex &index, int role)
+	{
+		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 rowCount(QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid() || !sourceModel()) ? 0 : sourceModel().rowCount(parent) * 2;
+	}
+
+	int columnCount(QModelIndex &parent = QModelIndex())
+	{
+		return (parent.isValid()) ? 0 : 1;
+	}
+
+	QModelIndex mapFromSource(QModelIndex &sourceIndex)
+	{
+		int row = sourceIndex.row() * 2;
+		return index(row, sourceIndex.column());
+	}
+
+
+	QModelIndex mapToSource(QModelIndex &proxyIndex)
+	{
+		if (!sourceModel())
+			return QModelIndex();
+		int row = proxyIndex.row() / 2;
+		return sourceModel().index(row, proxyIndex.column());
+	}
+
+	QModelIndex index(int, int, QModelIndex& = QModelIndex())
+	{
+		if (row < 0 || row >= rowCount(parent)
+		|| column < 0 || column >= columnCount(parent))
+			return QModelIndex();
+		return createIndex(row, column, 0);
+	}
+
+	QModelIndex parent(QModelIndex& index= QModelIndex());
+	{
+		return QModelIndex();
+	}
+
+	void setSourceModel(QAbstractItemModel *sourceModel);
+	{
+		if (sourceModel()) {
+			disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+			disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)),
+			this, SLOT(sourceReset()));
+			disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+			this, SLOT(sourceReset()));
+		}
+
+		QAbstractProxyModel::setSourceModel(newSourceModel);
+
+		if (newSourceModel) {
+			connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset()));
+			connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)),
+			this, SLOT(sourceReset()));
+			connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+			this, SLOT(sourceReset()));
+		}
+
+		reset();
+	}
+
+private slots:
+
+	void sourceReset()
+	{
+		reset();
+	}
+}
+
+// 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:
+
+	this(QAbstractItemModel *sourceModel, QObject *parent = null)
+	{
+		super(parent);
+		setSourceModel(sourceModel);
+	}
+
+	QVariant data(QModelIndex &index, int role = Qt.DisplayRole);
+	{
+		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 columnCount(QModelIndex &parent);
+	{
+		return sourceModel().columnCount(mapToSource(parent));
+	}
+
+
+    int rowCount(QModelIndex &parent = QModelIndex())
+{
+    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);
+}
+
+
+	QModelIndex mapFromSource(QModelIndex &sourceIndex);
+	{
+		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);
+	}
+
+
+	QModelIndex mapToSource(QModelIndex &proxyIndex)
+	{
+		int offset = proxyIndex.internalId();
+		if (offset == 0)
+			return QModelIndex();
+		int startDateRow = sourceDateRow(offset - 1);
+		return sourceModel().index(startDateRow + proxyIndex.row(), proxyIndex.column());
+	}
+
+
+	QModelIndex index(int row, int column, QModelIndex &parent = QModelIndex())
+	{
+	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 parent(QModelIndex &index= QModelIndex())
+	{
+		int offset = index.internalId();
+		if (offset == 0 || !index.isValid())
+			return QModelIndex();
+		return createIndex(offset - 1, 0, 0);
+	}
+
+
+	bool hasChildren(QModelIndex &parent = QModelIndex())
+	{
+		QModelIndex grandparent = parent.parent();
+		if (!grandparent.isValid())
+			return true;
+		return false;
+	}
+
+
+	Qt.ItemFlags flags(QModelIndex &index)
+	{
+		if (!index.isValid())
+			return Qt.NoItemFlags;
+		return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled;
+	}
+
+    bool removeRows(int row, int count, QModelIndex &parent = QModelIndex());
+{
+    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;
+}
+
+
+    QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole)
+{
+    return sourceModel().headerData(section, orientation, role);
+}
+
+
+
+void setSourceModel(QAbstractItemModel *newSourceModel)
+{
+    if (sourceModel()) {
+        disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset()));
+        disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset()));
+        disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)),
+                this, SLOT(sourceRowsInserted(QModelIndex &, int, int)));
+        disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+                this, SLOT(sourceRowsRemoved(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(QModelIndex &, int, int)),
+                this, SLOT(sourceRowsInserted(QModelIndex &, int, int)));
+        connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)),
+                this, SLOT(sourceRowsRemoved(QModelIndex &, int, int)));
+    }
+
+    reset();
+}
+
+
+private slots:
+
+	void sourceReset()
+	{
+		m_sourceRowCache.clear();
+		reset();
+	}
+
+	void sourceRowsInserted(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();
+		}
+	}
+
+
+    void sourceRowsRemoved(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();
+    }
+}
+
+private:
+
+	// Translate the top level date row into the offset where that date starts
+	int sourceDateRow(int row)
+	{
+		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);
+	}
+
+	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:
+	this(QObject *parent = null)
+	{
+		super(parent);
+		setSortRole(HistoryModel.DateTimeRole);
+		setFilterCaseSensitivity(Qt.CaseInsensitive);
+	}
+	
+protected:
+	bool filterAcceptsRow(int source_row, QModelIndex &source_parent)
+	{
+		if (!source_parent.isValid())
+			return true;
+		return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+	}
+}
+
+#include "ui_history.h"
+
+class HistoryDialog : public QDialog, public Ui_HistoryDialog
+{
+    Q_OBJECT
+
+signals:
+    void openUrl(QUrl &url);
+
+public:
+
+	this(QWidget *parent = null, HistoryManager *history = null) : 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(QModelIndex&)),
+		this, SLOT(open()));
+		tree.setContextMenuPolicy(Qt.CustomContextMenu);
+		connect(tree, SIGNAL(customContextMenuRequested(QPoint &)),
+		this, SLOT(customContextMenuRequested(QPoint &)));
+	}
+
+private slots:
+
+	void customContextMenuRequested(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 open()
+	{
+		QModelIndex index = tree.currentIndex();
+		if (!index.parent().isValid())
+			return;
+		emit openUrl(index.data(HistoryModel.UrlRole).toUrl());
+	}
+
+	void copy()
+	{
+		QModelIndex index = tree.currentIndex();
+		if (!index.parent().isValid())
+			return;
+		QString url = index.data(HistoryModel.UrlStringRole).toString();
+
+		QClipboard *clipboard = QApplication::clipboard();
+		clipboard.setText(url);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/htmls/htmls.qrc	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+    <file>notfound.html</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/htmls/notfound.html	Sun May 17 18:49:59 2009 +0000
@@ -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 checking 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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/main.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import browserapplication;
+
+int main(char[][] args)
+{
+	Q_INIT_RESOURCE(data);
+	scope application = new BrowserApplication(args);
+	if (!application.isTheOnlyBrowser())
+		return 0;
+	application.newMainWindow();
+	return application.exec();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/modelmenu.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtGui.QMenu
+import QtCore.QAbstractItemModel;
+
+import modelmenu;
+
+import QtCore.QAbstractItemModel;
+import qdebug;
+
+
+
+// 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 = null)
+{
+	super(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()));
+}
+
+    void setModel(QAbstractItemModel *model)
+{
+    m_model = model;
+}
+
+    QAbstractItemModel *model() const
+{
+    return m_model;
+}
+
+    void setMaxRows(int max)
+{
+    m_maxRows = max;
+}
+
+    int maxRows() const
+{
+    return m_maxRows;
+}
+
+    void setFirstSeparator(int offset)
+{
+    m_firstSeparator = offset;
+}
+
+    int firstSeparator() const
+{
+    return m_firstSeparator;
+}
+
+    void setRootIndex(const QModelIndex &index)
+{
+    m_root = index;
+}
+    QModelIndex rootIndex() const
+{
+    return m_root;
+}
+
+    void setHoverRole(int role)
+{
+    m_hoverRole = role;
+}
+    int hoverRole() const
+{
+    return m_hoverRole;
+}
+
+    void setSeparatorRole(int role)
+{
+    m_separatorRole = role;
+}
+
+    int separatorRole() const
+{
+    return m_separatorRole;
+}
+
+    QAction *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);
+}
+
+protected:
+    // add any actions before the tree, return true if any actions are added.
+    virtual bool prePopulated()
+{
+    return false;
+}
+    // 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 = null, QMenu *menu = null)
+{
+    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();
+    }
+}
+
+private slots:
+Q_DECLARE_METATYPE(QModelIndex)
+void 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 triggered(QAction *action)
+{
+    QVariant v = action.data();
+    if (v.canConvert<QModelIndex>()) {
+        QModelIndex idx = qvariant_cast<QModelIndex>(v);
+        emit activated(idx);
+    }
+}
+
+    void 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);
+    }
+}
+
+private:
+    QAction *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;
+}
+
+    int m_maxRows;
+    int m_firstSeparator;
+    int m_maxWidth;
+    int m_hoverRole;
+    int m_separatorRole;
+    QAbstractItemModel *m_model;
+    QPersistentModelIndex m_root;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/networkaccessmanager.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+module networkaccessmanager;
+
+
+import QtNetwork.QNetworkAccessManager;
+
+import browserapplication;
+import browsermainwindow;
+import ui_passworddialog;
+import ui_proxy;
+
+import QtCore.QSettings;
+
+import QtGui.QDesktopServices;
+import QtGui.QDialog;
+import QtGui.QMessageBox;
+import QtGui.QStyle;
+import QtGui.QTextDocument;
+
+import QtNetwork.QAuthenticator;
+import QtNetwork.QNetworkDiskCache;
+import QtNetwork.QNetworkProxy;
+import QtNetwork.QNetworkReply;
+import QtNetwork.QSslError;
+
+
+class NetworkAccessManager : public QNetworkAccessManager
+{
+    Q_OBJECT
+
+public:
+    this(QObject *parent = null)
+{
+	super(parent);
+	
+    connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
+            SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
+    connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
+            SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)));
+version(QT_NO_OPENSSL) {
+    connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)),
+            SLOT(sslErrors(QNetworkReply*, const QList<QSslError>&)));
+}
+    loadSettings();
+
+    QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
+    QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
+    diskCache.setCacheDirectory(location);
+    setCache(diskCache);
+}
+
+private:
+    QList<QString> sslTrustedHostList;
+
+public slots:
+    void 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 = QNetworkProxy::Socks5Proxy;
+        else
+            proxy = 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);
+}
+
+
+private slots:
+    void 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 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());
+    }
+}
+
+version(QT_NO_OPENSSL) {
+void sslErrors(QNetworkReply *reply, const QList<QSslError> &error)
+{
+    // check if SSL certificate has been trusted already
+    QString replyHost = reply.url().host() + ":" + reply.url().port();
+    if(! sslTrustedHostList.contains(replyHost)) {
+        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 for this host?").arg(reply.url().toString()).arg(errors),
+                        QMessageBox::Yes | QMessageBox::No,
+                        QMessageBox::No);
+        if (ret == QMessageBox::Yes) {
+            reply.ignoreSslErrors();
+            sslTrustedHostList.append(replyHost);
+        }
+    }
+}
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/searchlineedit.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import urllineedit;
+
+import QtGui.QLineEdit;
+import QtGui.QAbstractButton;
+
+import searchlineedit;
+
+import QtGui.QPainter;
+import QtGui.QMouseEvent;
+import QtGui.QMenu;
+import QtGui.QStyle;
+import QtGui.QStyleOptionFrameV2;
+
+/*
+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:
+    this(QWidget *parent = null)
+{
+	super(parent);
+    setCursor(Qt.ArrowCursor);
+    setToolTip(tr("Clear"));
+    setVisible(false);
+    setFocusPolicy(Qt.NoFocus);
+}
+    void 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);
+}
+
+public slots:
+    void textChanged(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 = null)
+{
+	super(parent);
+	m_menu = 0;
+    setObjectName(QLatin1String("SearchButton"));
+    setCursor(Qt.ArrowCursor);
+    setFocusPolicy(Qt.NoFocus);
+}
+
+    void 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();
+}
+    QMenu *m_menu;
+
+protected:
+    void 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);
+}
+}
+
+class SearchLineEdit : public ExLineEdit
+{
+    Q_OBJECT
+    Q_PROPERTY(QString inactiveText READ inactiveText WRITE setInactiveText)
+
+signals:
+    void textChanged(QString &text);
+
+public:
+/*
+    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
+ */
+this(QWidget *parent = null) : ExLineEdit(parent),
+    m_searchButton(new SearchButton(this))
+{
+    connect(lineEdit(), SIGNAL(textChanged(QString &)),
+            this, SIGNAL(textChanged(QString &)));
+    setLeftWidget(m_searchButton);
+    m_inactiveText = tr("Search");
+
+    QSizePolicy policy = sizePolicy();
+    setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy());
+}
+
+    QString inactiveText()
+{
+    return m_inactiveText;
+}
+
+    void setInactiveText(QString &text)
+{
+    m_inactiveText = text;
+}
+
+    QMenu *menu()
+{
+    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;
+}
+
+    void setMenu(QMenu *menu)
+{
+    if (m_searchButton.m_menu)
+        m_searchButton.m_menu.deleteLater();
+    m_searchButton.m_menu = menu;
+    updateGeometries();
+}
+
+protected:
+    void resizeEvent(QResizeEvent *event)
+{
+    updateGeometries();
+    ExLineEdit.resizeEvent(event);
+}
+
+    void 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);
+    }
+}
+
+private:
+    void updateGeometries()
+{
+    int menuHeight = height();
+    int menuWidth = menuHeight + 1;
+    if (!m_searchButton.m_menu)
+        menuWidth = (menuHeight / 5) * 4;
+    m_searchButton.resize(QSize(menuWidth, menuHeight));
+}
+
+    SearchButton *m_searchButton;
+    QString m_inactiveText;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/settings.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,339 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module settings;
+
+
+import QtGui.QDialog;
+import ui_settings;
+
+
+import settings;
+
+import browserapplication;
+import browsermainwindow;
+import cookiejar;
+import history;
+import networkaccessmanager;
+import webview;
+
+import QtCore.QSettings;
+import QtGui.QtGui;
+import QtWebKit.QtWebKit;
+
+
+class SettingsDialog : public QDialog, public Ui_Settings
+{
+    Q_OBJECT
+
+public:
+    SettingsDialog(QWidget *parent = null);
+    : 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 accept();
+{
+    saveToSettings();
+    QDialog::accept();
+}
+
+private slots:
+    void 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 loadFromSettings()
+{
+    QSettings settings;
+    settings.beginGroup(QLatin1String("MainWindow"));
+    QString defaultHome = QLatin1String("http://qtsoftware.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 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 setHomeToCurrentPage()
+{
+    BrowserMainWindow *mw = static_cast<BrowserMainWindow*>(parent());
+    WebView *webView = mw.currentTab();
+    if (webView)
+        homeLineEdit.setText(webView.url().toString());
+}
+
+    void showCookies()
+{
+    CookiesDialog *dialog = new CookiesDialog(BrowserApplication::cookieJar(), this);
+    dialog.exec();
+}
+    void showExceptions()
+{
+    CookiesExceptionsDialog *dialog = new CookiesExceptionsDialog(BrowserApplication::cookieJar(), this);
+    dialog.exec();
+}
+
+    void 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 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()));
+    }
+}
+
+private:
+    QFont standardFont;
+    QFont fixedFont;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/squeezelabel.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module squeezelabel;
+
+
+import QtGui.QLabel;
+
+
+class SqueezeLabel : public QLabel
+{
+    Q_OBJECT
+
+public:
+this(QWidget *parent = null)
+{
+	super(parent);
+}
+
+protected:
+    void 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);
+    }
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/tabwidget.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,952 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtGui.QTabBar;
+
+import QtGui.QShortcut;
+
+import tabwidget;
+
+import browserapplication;
+import browsermainwindow;
+import history;
+import urllineedit;
+import webview;
+
+import QtGui.QClipboard;
+import QtGui.QCompleter;
+import QtGui.QListView;
+import QtGui.QMenu;
+import QtGui.QMessageBox;
+import QtGui.QMouseEvent;
+import QtGui.QStackedWidget;
+import QtGui.QStyle;
+import QtGui.QToolButton;
+
+import QtCore.QDebug;
+
+/*
+    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)
+{
+    if (-1 == index)
+        return;
+    for (int i = count() - 1; i > index; --i)
+        closeTab(i);
+    for (int i = index - 1; i >= 0; --i)
+        closeTab(i);
+}
+
+    void reloadTab(int index);
+    void reloadAllTabs();
+    void tabMoveRequested(int fromIndex, int toIndex);
+
+public:
+    this(QWidget *parent = null)
+{
+	super(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()));
+    }
+    setTabsClosable(true);
+    connect(this, SIGNAL(tabCloseRequested(int)),
+            this, SIGNAL(closeTab(int)));
+    setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
+    setMovable(true);
+}
+
+protected:
+    void mousePressEvent(QMouseEvent* event)
+{
+    if (event.button() == Qt.LeftButton)
+        m_dragStartPos = event.pos();
+    QTabBar::mousePressEvent(event);
+}
+
+    void mouseMoveEvent(QMouseEvent* event)
+{
+    if (event.buttons() == Qt.LeftButton) {
+        int diffX = event.pos().x() - m_dragStartPos.x();
+        int diffY = event.pos().y() - m_dragStartPos.y();
+        if ((event.pos() - m_dragStartPos).manhattanLength() > QApplication::startDragDistance()
+            && diffX < 3 && diffX > -3
+            && diffY < -10) {
+            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);
+}
+
+private slots:
+    void selectTabAction()
+{
+    if (QShortcut *shortCut = qobject_cast<QShortcut*>(sender())) {
+        int index = m_tabShortcuts.indexOf(shortCut);
+        if (index == 0)
+            index = 10;
+        setCurrentIndex(index);
+    }
+}
+    void cloneTab();
+{
+    if (QAction *action = qobject_cast<QAction*>(sender())) {
+        int index = action.data().toInt();
+        emit cloneTab(index);
+    }
+}
+
+    void closeTab();
+{
+    if (QAction *action = qobject_cast<QAction*>(sender())) {
+        int index = action.data().toInt();
+        emit closeTab(index);
+    }
+}
+
+    void closeOtherTabs()
+{
+    if (QAction *action = qobject_cast<QAction*>(sender())) {
+        int index = action.data().toInt();
+        emit closeOtherTabs(index);
+    }
+}
+
+void reloadTab()
+{
+    if (QAction *action = qobject_cast<QAction*>(sender())) {
+        int index = action.data().toInt();
+        emit reloadTab(index);
+    }
+}
+
+    void 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());
+}
+
+private:
+    QList<QShortcut*> m_tabShortcuts;
+    friend class TabWidget;
+
+    QPoint m_dragStartPos;
+    int m_dragCurrentIndex;
+}
+
+import 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:
+    this(QAction *root, QWebPage::WebAction webAction, QObject *parent)
+{
+super(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);
+}
+
+    QWebPage::WebAction webAction() const
+{
+    return m_webAction;
+}
+
+    void addChild(QAction *action)
+{
+    if (!action)
+        return;
+    connect(action, SIGNAL(changed()), this, SLOT(childChanged()));
+}
+
+
+    void 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()));
+}
+
+private slots:
+    void rootTriggered()
+{
+    if (m_currentParent) {
+        QAction *gotoAction = m_currentParent.action(m_webAction);
+        gotoAction.trigger();
+    }
+}
+
+    void 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 rootDestroyed()
+{
+    m_root = 0;
+}
+    void currentDestroyed()
+{
+    updateCurrent(0);
+}
+
+private:
+    QWebPage *m_currentParent;
+    QAction *m_root;
+    QWebPage::WebAction m_webAction;
+};
+
+import QtCore.QUrl;
+import 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:
+    this(QWidget *parent = null)
+{
+	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(tabMoved(int, int)), this, SLOT(moveTab(int, int)));
+    setTabBar(m_tabBar);
+    setDocumentMode(true);
+
+    // 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);
+
+    connect(this, SIGNAL(currentChanged(int)),
+            this, SLOT(currentChanged(int)));
+
+    m_lineEdits = new QStackedWidget(this);
+}
+
+    void 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 addWebAction(QAction *action, QWebPage::WebAction webAction)
+{
+    if (!action)
+        return;
+    m_actions.append(new WebActionMapper(action, webAction, this));
+}
+
+
+    QAction *newTabAction() const;
+{
+    return m_newTabAction;
+}
+
+    QAction *closeTabAction() const;
+{
+    return m_closeTabAction;
+}
+    QAction *recentlyClosedTabsAction() const;
+{
+    return m_recentlyClosedTabsAction;
+}
+
+    QAction *nextTabAction() const
+{
+    return m_nextTabAction;
+}
+    QAction *previousTabAction() const
+{
+    return m_previousTabAction;
+}
+
+    QWidget *lineEditStack() const
+{
+    return m_lineEdits;
+}
+
+    QLineEdit *currentLineEdit() const
+{
+    return lineEdit(m_lineEdits.currentIndex());
+}
+
+    WebView *currentWebView() const
+{
+    return webView(currentIndex());
+}
+
+    WebView *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;
+}
+
+    QLineEdit *lineEdit(int index) const
+{
+    UrlLineEdit *urlLineEdit = qobject_cast<UrlLineEdit*>(m_lineEdits.widget(index));
+    if (urlLineEdit)
+        return urlLineEdit.lineEdit();
+    return 0;
+}
+
+    int webViewIndex(WebView *webView) const
+{
+    int index = indexOf(webView);
+    return index;
+}
+
+    
+static const qint32 TabWidgetMagic = 0xaa;
+
+QByteArray 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 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;
+}
+
+protected:
+    void 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 contextMenuEvent(QContextMenuEvent *event)
+{
+    if (!childAt(event.pos())) {
+        m_tabBar.contextMenuRequested(event.pos());
+        return;
+    }
+    QTabWidget::contextMenuEvent(event);
+}
+
+    void 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);
+        }
+    }
+}
+
+public slots:
+    void loadUrlInCurrentTab(const QUrl &url)
+{
+    WebView *webView = currentWebView();
+    if (webView) {
+        webView.loadUrl(url);
+        webView.setFocus();
+    }
+}
+
+    WebView *newTab(bool makeCurrent = true)
+{
+    // 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;
+}
+
+
+// When index is -1 index chooses the current tab
+    void cloneTab(int index = -1)
+{
+    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 closeTab(int index = -1);
+{
+    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 closeOtherTabs(int index);
+// When index is -1 index chooses the current tab
+    void reloadTab(int index = -1);
+{
+    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 reloadAllTabs();
+{
+    for (int i = 0; i < count(); ++i) {
+        QWidget *tabWidget = widget(i);
+        if (WebView *tab = qobject_cast<WebView*>(tabWidget)) {
+            tab.reload();
+        }
+    }
+}
+    void nextTab()
+{
+    int next = currentIndex() + 1;
+    if (next == count())
+        next = 0;
+    setCurrentIndex(next);
+}
+
+
+    void previousTab()
+{
+    int next = currentIndex() - 1;
+    if (next < 0)
+        next = count() - 1;
+    setCurrentIndex(next);
+}
+
+private slots:
+    void currentChanged(int index)
+{
+    WebView *webView = this.webView(index);
+    if (!webView)
+        return;
+
+    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();
+}
+
+    void 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 aboutToShowRecentTriggeredAction(QAction *action)
+{
+    QUrl url = action.data().toUrl();
+    loadUrlInCurrentTab(url);
+}
+
+    void webViewLoadStarted()
+{
+    WebView *webView = qobject_cast<WebView*>(sender());
+    int index = webViewIndex(webView);
+    if (-1 != index) {
+        QIcon icon(QLatin1String(":loading.gif"));
+        setTabIcon(index, icon);
+    }
+}
+
+    void 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 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 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 lineEditReturnPressed()
+{
+    if (QLineEdit *lineEdit = qobject_cast<QLineEdit*>(sender())) {
+        emit loadPage(lineEdit.text());
+        if (m_lineEdits.currentWidget() == lineEdit)
+            currentWebView().setFocus();
+    }
+}
+
+    void 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 moveTab(int fromIndex, int toIndex)
+{
+    QWidget *lineEdit = m_lineEdits.widget(fromIndex);
+    m_lineEdits.removeWidget(lineEdit);
+    m_lineEdits.insertWidget(toIndex, lineEdit);
+}
+
+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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/toolbarsearch.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module toolbarsearch;
+
+import searchlineedit;
+
+import autosaver;
+
+import QtCore.QSettings;
+import QtCore.QUrl;
+
+import QtGui.QCompleter;
+import QtGui.QMenu;
+import QtGui.QStringListModel;
+
+import QtWebKit/QWebSettings;
+
+/*
+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 is a very basic search widget that also contains a small history.
+    Searches are turned into urls that use Google to perform search
+ */
+this(QWidget *parent = null)
+ 
+{
+	super(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();
+}
+    ~this()
+{
+    m_autosaver.saveIfNeccessary();
+}
+
+public slots:
+    void clear()
+{
+    m_stringListModel.setStringList(QStringList());
+    m_autosaver.changeOccurred();;
+}
+
+    void 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);
+}
+
+private slots:
+    void save()
+{
+    QSettings settings;
+    settings.beginGroup(QLatin1String("toolbarsearch"));
+    settings.setValue(QLatin1String("recentSearches"), m_stringListModel.stringList());
+    settings.setValue(QLatin1String("maximumSaved"), m_maxSavedSearches);
+    settings.endGroup();
+}
+
+
+    void 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 triggeredMenuAction(QAction *action)
+{
+    QVariant v = action.data();
+    if (v.canConvert<QString>()) {
+        QString text = v.toString();
+        lineEdit().setText(text);
+        searchNow();
+    }
+}
+
+private:
+    void 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();
+}
+
+    AutoSaver *m_autosaver;
+    int m_maxSavedSearches;
+    QStringListModel *m_stringListModel;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/urllineedit.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+module urllineedit;
+
+import QtCore.QUrl;
+import QtGui.QWidget;
+import QtGui.QStyleOptionFrame;
+
+import browserapplication;
+import searchlineedit;
+import webview;
+
+import QtCore.QEvent;
+
+import QtGui.QApplication;
+import QtGui.QCompleter;
+import QtGui.QFocusEvent;
+import QtGui.QHBoxLayout;
+import QtGui.QLabel;
+import QtGui.QLineEdit;
+import QtGui.QPainter;
+import QtGui.QStyle;
+import QtGui.QStyleOptionFrameV2;
+
+import QtCore.QDebug;
+
+
+/*
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+QT_END_NAMESPACE
+
+class ClearButton;
+*/
+class ExLineEdit : public QWidget
+{
+    Q_OBJECT
+
+public:
+    this(QWidget *parent = null)
+{
+	super(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&)));
+}
+
+    inline QLineEdit *lineEdit() { return m_lineEdit; }
+
+    void setLeftWidget(QWidget *widget)
+{
+    m_leftWidget = widget;
+}
+
+    QWidget *leftWidget()
+{
+    return m_leftWidget;
+}
+
+    QSize sizeHint()
+{
+    m_lineEdit.setFrame(true);
+    QSize size = m_lineEdit.sizeHint();
+    m_lineEdit.setFrame(false);
+    return size;
+}
+
+    QVariant inputMethodQuery(Qt.InputMethodQuery property)
+{
+    return m_lineEdit.inputMethodQuery(property);
+}
+
+protected:
+    void focusInEvent(QFocusEvent *event)
+{
+    m_lineEdit.event(event);
+    QWidget::focusInEvent(event);
+}
+
+    void 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 keyPressEvent(QKeyEvent *event)
+{
+    m_lineEdit.event(event);
+}
+
+    void paintEvent(QPaintEvent *event)
+{
+    QPainter p(this);
+    QStyleOptionFrameV2 panel;
+    initStyleOption(&panel);
+    style().drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this);
+}
+
+    void resizeEvent(QResizeEvent *event)
+{
+    Q_ASSERT(m_leftWidget);
+    updateGeometries();
+    QWidget::resizeEvent(event);
+}
+
+    void inputMethodEvent(QInputMethodEvent *e)
+{
+    m_lineEdit.event(e);
+}
+
+    bool event(QEvent *event)
+{
+    if (event.type() == QEvent::ShortcutOverride)
+        return m_lineEdit.event(event);
+    return QWidget::event(event);
+}
+
+protected:
+    void 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 initStyleOption(QStyleOptionFrameV2 *option)
+{
+    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;
+version(QT_KEYPAD_NAVIGATION)
+    if (hasEditFocus())
+        option.state |= QStyle::State_HasEditFocus;
+}
+    option.features = QStyleOptionFrameV2::None;
+}
+
+    QWidget *m_leftWidget;
+    QLineEdit *m_lineEdit;
+    ClearButton *m_clearButton;
+};
+
+class UrlIconLabel : public QLabel
+{
+
+public:
+    this(QWidget *parent)
+{
+	super(parent);
+	m_webView = 0;
+    setMinimumWidth(16);
+    setMinimumHeight(16);
+}
+
+
+    WebView *m_webView;
+
+protected:
+    void mousePressEvent(QMouseEvent *event)
+{
+    if (event.button() == Qt.LeftButton)
+        m_dragStartPos = event.pos();
+    QLabel::mousePressEvent(event);
+}
+
+    void 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(QString::fromUtf8(m_webView.url().toEncoded()));
+        QList<QUrl> urls;
+        urls.append(m_webView.url());
+        mimeData.setUrls(urls);
+        drag.setMimeData(mimeData);
+        drag.exec();
+    }
+}
+
+private:
+    QPoint m_dragStartPos;
+
+}
+
+
+//class UrlIconLabel;
+//class WebView;
+class UrlLineEdit : public ExLineEdit
+{
+    Q_OBJECT
+
+public:
+    this(QWidget *parent = null)
+{
+	super(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 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()));
+}
+
+protected:
+    void 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);
+    }
+}
+
+    void focusOutEvent(QFocusEvent *event);
+{
+    if (m_lineEdit.text().isEmpty() && m_webView)
+        m_lineEdit.setText(QString::fromUtf8(m_webView.url().toEncoded()));
+    ExLineEdit::focusOutEvent(event);
+}
+
+private slots:
+    void webViewUrlChanged(const QUrl &url)
+{
+    m_lineEdit.setText(QString::fromUtf8(url.toEncoded()));
+    m_lineEdit.setCursorPosition(0);
+}
+
+    void 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);
+}
+
+private:
+    QLinearGradient generateGradient(const QColor &color)
+{
+    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;
+}
+
+    WebView *m_webView;
+    UrlIconLabel *m_iconLabel;
+    QColor m_defaultBaseColor;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/webview.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtWebKit.QWebView;
+
+import browserapplication;
+import browsermainwindow;
+import cookiejar;
+import downloadmanager;
+import networkaccessmanager;
+import tabwidget;
+import webview;
+
+import QtGui.QClipboard;
+import QtGui.QMenu;
+import QtGui.QMessageBox;
+import QtGui.QMouseEvent;
+
+import QtWebKit.QWebHitTestResult;
+
+import QtUiTools.QUiLoader;
+
+import QtCore.QDebug;
+import QtCore.QBuffer;
+
+/*
+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:
+    this(QObject *parent = null)
+{
+	super(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 *mainWindow()
+{
+    QObject *w = this.parent();
+    while (w) {
+        if (BrowserMainWindow *mw = qobject_cast<BrowserMainWindow*>(w))
+            return mw;
+        w = w.parent();
+    }
+    return BrowserApplication::instance().mainWindow();
+}
+
+protected:
+    bool 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 *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();
+}
+
+version(QT_NO_UITOOLS) {} else
+{
+	QObject *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());
+	}
+}
+
+
+
+private slots:
+    void 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());
+    }
+}
+
+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 = null)
+    : 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);
+
+}
+
+    WebPage *webPage() const { return m_page; }
+
+    void loadUrl(const QUrl &url)
+{
+    m_initialUrl = url;
+    load(url);
+}
+    
+    QUrl url() const
+{
+    QUrl url = QWebView::url();
+    if (!url.isEmpty())
+        return url;
+
+    return m_initialUrl;
+}
+
+    QString lastStatusBarText() const
+{
+    return m_statusBarText;
+}
+
+    inline int progress() const { return m_progress; }
+
+protected:
+    void mousePressEvent(QMouseEvent *event);
+{
+    m_page.m_pressedButtons = event.buttons();
+    m_page.m_keyboardModifiers = event.modifiers();
+    QWebView::mousePressEvent(event);
+}
+
+    void 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 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 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);
+}
+
+
+private slots:
+    void setProgress(int progress)
+{
+    m_progress = progress;
+}
+
+    void loadFinished()
+{
+    if (100 != m_progress) {
+        qWarning() << "Recieved finished signal while progress is still:" << progress()
+                   << "Url:" << url();
+    }
+    m_progress = 0;
+}
+
+    void setStatusBarText(const QString &string)
+{
+    m_statusBarText = string;
+}
+
+    void downloadRequested(const QNetworkRequest &request)
+{
+    BrowserApplication::downloadManager().download(request);
+}
+
+    void openLinkInNewTab()
+{
+    m_page.m_openInNewTab = true;
+    pageAction(QWebPage::OpenLinkInNewWindow).trigger();
+}
+private:
+    QString m_statusBarText;
+    QUrl m_initialUrl;
+    int m_progress;
+    WebPage *m_page;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/browser/xbel.d	Sun May 17 18:49:59 2009 +0000
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 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 the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+module xbel;
+
+import QtCore.QXmlStreamReader;
+import QtCore.QDateTime;
+
+
+import QtCore.QFile;
+
+class BookmarkNode
+{
+public:
+    enum Type {
+        Root,
+        Folder,
+        Bookmark,
+        Separator
+    };
+
+    this(Type type = Root, BookmarkNode *parent = null)
+{
+	 expanded = false;
+   m_parent = parent;
+   m_type = type;
+	
+    if (parent)
+        parent.add(this);
+}
+    
+    ~this()
+{
+    if (m_parent)
+        m_parent.remove(this);
+    qDeleteAll(m_children);
+    m_parent = 0;
+    m_type = BookmarkNode::Root;
+}
+
+    bool 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;
+}
+
+    Type type() const
+{
+    return m_type;
+}
+
+    void setType(Type type)
+{
+    m_type = type;
+}
+
+    QList<BookmarkNode *> children() const
+{
+    return m_children;
+}
+
+
+    BookmarkNode *parent() const
+{
+    return m_parent;
+}
+
+    void add(BookmarkNode *child, int offset = -1)
+{
+    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 remove(BookmarkNode *child)
+{
+    child.m_parent = 0;
+    m_children.removeAll(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:
+this()
+{
+}
+
+BookmarkNode *read(const QString &fileName)
+{
+    QFile file(fileName);
+    if (!file.exists()) {
+        return new BookmarkNode(BookmarkNode::Root);
+    }
+    file.open(QFile::ReadOnly);
+    return read(&file);
+}
+
+BookmarkNode *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;
+}
+
+private:
+    void skipUnknownElement()
+{
+    Q_ASSERT(isStartElement());
+
+    while (!atEnd()) {
+        readNext();
+
+        if (isEndElement())
+            break;
+
+        if (isStartElement())
+            skipUnknownElement();
+    }
+}
+
+    void 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 readTitle(BookmarkNode *parent)
+{
+    Q_ASSERT(isStartElement() && name() == QLatin1String("title"));
+    parent.title = readElementText();
+}
+
+    void readDescription(BookmarkNode *parent)
+{
+    Q_ASSERT(isStartElement() && name() == QLatin1String("desc"));
+    parent.desc = readElementText();
+}
+
+    void readSeparator(BookmarkNode *parent)
+{
+    new BookmarkNode(BookmarkNode::Separator, parent);
+    // empty elements have a start and end element
+    readNext();
+}
+
+
+    void 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 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");
+}
+
+};
+
+import QtCore.QXmlStreamWriter;
+
+class XbelWriter : public QXmlStreamWriter
+{
+public:
+    this()
+{
+    setAutoFormatting(true);
+}
+    bool write(const QString &fileName, const BookmarkNode *root);
+{
+    QFile file(fileName);
+    if (!root || !file.open(QFile::WriteOnly))
+        return false;
+    return write(&file, root);
+}
+
+bool 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;
+}
+
+
+private:
+    void 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;
+    }
+}
+}