Mercurial > projects > qtd
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; +}