diff demos/browser/bookmarks.d @ 45:71b382c10ef6

add coarse and incomplete QT browser port
author mandel
date Sun, 17 May 2009 18:49:59 +0000
parents
children b149ef2cb18b
line wrap: on
line diff
--- /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;
+}