bookmarks.cpp Example File | Qt WebEngine 5.9

Qt 5.9 | Qt WebEngine | WebEngine Demo Browser Example | bookmarks.cpp Example File

Qt 5.9.9 Reference Documentation

# bookmarks.cpp Example File

webenginewidgets/demobrowser/bookmarks.cpp

      /****************************************************************************
      **
      ** Copyright (C) 2016 The Qt Company Ltd.
      ** Contact: https://www.qt.io/licensing/
      **
      ** This file is part of the examples of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** Commercial License Usage
      ** Licensees holding valid commercial Qt licenses may use this file in
      ** accordance with the commercial license agreement provided with the
      ** Software or, alternatively, in accordance with the terms contained in
      ** a written agreement between you and The Qt Company. For licensing terms
      ** and conditions see https://www.qt.io/terms-conditions. For further
      ** information use the contact form at https://www.qt.io/contact-us.
      **
      ** BSD License Usage
      ** Alternatively, you may use this file under the terms of the BSD license
      ** as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      #include "bookmarks.h"

      #include "autosaver.h"
      #include "browserapplication.h"
      #include "history.h"
      #include "xbel.h"

      #include <QtCore/QBuffer>
      #include <QtCore/QFile>
      #include <QtCore/QMimeData>

      #include <QtGui/QDesktopServices>
      #include <QtGui/QDragEnterEvent>
      #include <QtGui/QIcon>
      #include <QtWidgets/QFileDialog>
      #include <QtWidgets/QHeaderView>
      #include <QtWidgets/QMessageBox>
      #include <QtWidgets/QToolButton>

      #include <QtCore/QDebug>

      #define BOOKMARKBAR "Bookmarks Bar"
      #define BOOKMARKMENU "Bookmarks Menu"

      BookmarksManager::BookmarksManager(QObject *parent)
          : QObject(parent)
          , m_loaded(false)
          , m_saveTimer(new AutoSaver(this))
          , m_bookmarkRootNode(0)
          , m_bookmarkModel(0)
      {
          connect(this, SIGNAL(entryAdded(BookmarkNode*)),
                  m_saveTimer, SLOT(changeOccurred()));
          connect(this, SIGNAL(entryRemoved(BookmarkNode*,int,BookmarkNode*)),
                  m_saveTimer, SLOT(changeOccurred()));
          connect(this, SIGNAL(entryChanged(BookmarkNode*)),
                  m_saveTimer, SLOT(changeOccurred()));
      }

      BookmarksManager::~BookmarksManager()
      {
          m_saveTimer->saveIfNeccessary();
      }

      void BookmarksManager::changeExpanded()
      {
          m_saveTimer->changeOccurred();
      }

      void BookmarksManager::load()
      {
          if (m_loaded)
              return;
          m_loaded = true;

          QString dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
          QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
          if (!QFile::exists(bookmarkFile))
              bookmarkFile = QLatin1String(":defaultbookmarks.xbel");

          XbelReader reader;
          m_bookmarkRootNode = reader.read(bookmarkFile);
          if (reader.error() != QXmlStreamReader::NoError) {
              QMessageBox::warning(0, QLatin1String("Loading Bookmark"),
                  tr("Error when loading bookmarks on line %1, column %2:\n"
                     "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
          }

          BookmarkNode *toolbar = 0;
          BookmarkNode *menu = 0;
          QList<BookmarkNode*> others;
          for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
              BookmarkNode *node = m_bookmarkRootNode->children().at(i);
              if (node->type() == BookmarkNode::Folder) {
                  // Automatically convert
                  if (node->title == tr("Toolbar Bookmarks") && !toolbar) {
                      node->title = tr(BOOKMARKBAR);
                  }
                  if (node->title == tr(BOOKMARKBAR) && !toolbar) {
                      toolbar = node;
                  }

                  // Automatically convert
                  if (node->title == tr("Menu") && !menu) {
                      node->title = tr(BOOKMARKMENU);
                  }
                  if (node->title == tr(BOOKMARKMENU) && !menu) {
                      menu = node;
                  }
              } else {
                  others.append(node);
              }
              m_bookmarkRootNode->remove(node);
          }
          Q_ASSERT(m_bookmarkRootNode->children().count() == 0);
          if (!toolbar) {
              toolbar = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode);
              toolbar->title = tr(BOOKMARKBAR);
          } else {
              m_bookmarkRootNode->add(toolbar);
          }

          if (!menu) {
              menu = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode);
              menu->title = tr(BOOKMARKMENU);
          } else {
              m_bookmarkRootNode->add(menu);
          }

          for (int i = 0; i < others.count(); ++i)
              menu->add(others.at(i));
      }

      void BookmarksManager::save() const
      {
          if (!m_loaded)
              return;

          XbelWriter writer;
          QString dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
          QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
          if (!writer.write(bookmarkFile, m_bookmarkRootNode))
              qWarning() << "BookmarkManager: error saving to" << bookmarkFile;
      }

      void BookmarksManager::addBookmark(BookmarkNode *parent, BookmarkNode *node, int row)
      {
          if (!m_loaded)
              return;
          Q_ASSERT(parent);
          InsertBookmarksCommand *command = new InsertBookmarksCommand(this, parent, node, row);
          m_commands.push(command);
      }

      void BookmarksManager::removeBookmark(BookmarkNode *node)
      {
          if (!m_loaded)
              return;

          Q_ASSERT(node);
          BookmarkNode *parent = node->parent();
          int row = parent->children().indexOf(node);
          RemoveBookmarksCommand *command = new RemoveBookmarksCommand(this, parent, row);
          m_commands.push(command);
      }

      void BookmarksManager::setTitle(BookmarkNode *node, const QString &newTitle)
      {
          if (!m_loaded)
              return;

          Q_ASSERT(node);
          ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newTitle, true);
          m_commands.push(command);
      }

      void BookmarksManager::setUrl(BookmarkNode *node, const QString &newUrl)
      {
          if (!m_loaded)
              return;

          Q_ASSERT(node);
          ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newUrl, false);
          m_commands.push(command);
      }

      BookmarkNode *BookmarksManager::bookmarks()
      {
          if (!m_loaded)
              load();
          return m_bookmarkRootNode;
      }

      BookmarkNode *BookmarksManager::menu()
      {
          if (!m_loaded)
              load();

          for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
              BookmarkNode *node = m_bookmarkRootNode->children().at(i);
              if (node->title == tr(BOOKMARKMENU))
                  return node;
          }
          Q_ASSERT(false);
          return 0;
      }

      BookmarkNode *BookmarksManager::toolbar()
      {
          if (!m_loaded)
              load();

          for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
              BookmarkNode *node = m_bookmarkRootNode->children().at(i);
              if (node->title == tr(BOOKMARKBAR))
                  return node;
          }
          Q_ASSERT(false);
          return 0;
      }

      BookmarksModel *BookmarksManager::bookmarksModel()
      {
          if (!m_bookmarkModel)
              m_bookmarkModel = new BookmarksModel(this, this);
          return m_bookmarkModel;
      }

      void BookmarksManager::importBookmarks()
      {
          QString fileName = QFileDialog::getOpenFileName(0, tr("Open File"),
                                                           QString(),
                                                           tr("XBEL (*.xbel *.xml)"));
          if (fileName.isEmpty())
              return;

          XbelReader reader;
          BookmarkNode *importRootNode = reader.read(fileName);
          if (reader.error() != QXmlStreamReader::NoError) {
              QMessageBox::warning(0, QLatin1String("Loading Bookmark"),
                  tr("Error when loading bookmarks on line %1, column %2:\n"
                     "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
          }

          importRootNode->setType(BookmarkNode::Folder);
          importRootNode->title = (tr("Imported %1").arg(QDate::currentDate().toString(Qt::SystemLocaleShortDate)));
          addBookmark(menu(), importRootNode);
      }

      void BookmarksManager::exportBookmarks()
      {
          QString fileName = QFileDialog::getSaveFileName(0, tr("Save File"),
                                      tr("%1 Bookmarks.xbel").arg(QCoreApplication::applicationName()),
                                      tr("XBEL (*.xbel *.xml)"));
          if (fileName.isEmpty())
              return;

          XbelWriter writer;
          if (!writer.write(fileName, m_bookmarkRootNode))
              QMessageBox::critical(0, tr("Export error"), tr("error saving bookmarks"));
      }

      RemoveBookmarksCommand::RemoveBookmarksCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row)
          : QUndoCommand(BookmarksManager::tr("Remove Bookmark"))
          , m_row(row)
          , m_bookmarkManagaer(m_bookmarkManagaer)
          , m_node(parent->children().value(row))
          , m_parent(parent)
          , m_done(false)
      {
      }

      RemoveBookmarksCommand::~RemoveBookmarksCommand()
      {
          if (m_done && !m_node->parent()) {
              delete m_node;
          }
      }

      void RemoveBookmarksCommand::undo()
      {
          m_parent->add(m_node, m_row);
          emit m_bookmarkManagaer->entryAdded(m_node);
          m_done = false;
      }

      void RemoveBookmarksCommand::redo()
      {
          m_parent->remove(m_node);
          emit m_bookmarkManagaer->entryRemoved(m_parent, m_row, m_node);
          m_done = true;
      }

      InsertBookmarksCommand::InsertBookmarksCommand(BookmarksManager *m_bookmarkManagaer,
                      BookmarkNode *parent, BookmarkNode *node, int row)
          : RemoveBookmarksCommand(m_bookmarkManagaer, parent, row)
      {
          setText(BookmarksManager::tr("Insert Bookmark"));
          m_node = node;
      }

      ChangeBookmarkCommand::ChangeBookmarkCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *node,
                              const QString &newValue, bool title)
          : QUndoCommand()
          , m_bookmarkManagaer(m_bookmarkManagaer)
          , m_title(title)
          , m_newValue(newValue)
          , m_node(node)
      {
          if (m_title) {
              m_oldValue = m_node->title;
              setText(BookmarksManager::tr("Name Change"));
          } else {
              m_oldValue = m_node->url;
              setText(BookmarksManager::tr("Address Change"));
          }
      }

      void ChangeBookmarkCommand::undo()
      {
          if (m_title)
              m_node->title = m_oldValue;
          else
              m_node->url = m_oldValue;
          emit m_bookmarkManagaer->entryChanged(m_node);
      }

      void ChangeBookmarkCommand::redo()
      {
          if (m_title)
              m_node->title = m_newValue;
          else
              m_node->url = m_newValue;
          emit m_bookmarkManagaer->entryChanged(m_node);
      }

      BookmarksModel::BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent)
          : QAbstractItemModel(parent)
          , m_endMacro(false)
          , m_bookmarksManager(bookmarkManager)
      {
          connect(bookmarkManager, SIGNAL(entryAdded(BookmarkNode*)),
                  this, SLOT(entryAdded(BookmarkNode*)));
          connect(bookmarkManager, SIGNAL(entryRemoved(BookmarkNode*,int,BookmarkNode*)),
                  this, SLOT(entryRemoved(BookmarkNode*,int,BookmarkNode*)));
          connect(bookmarkManager, SIGNAL(entryChanged(BookmarkNode*)),
                  this, SLOT(entryChanged(BookmarkNode*)));
      }

      QModelIndex BookmarksModel::index(BookmarkNode *node) const
      {
          BookmarkNode *parent = node->parent();
          if (!parent)
              return QModelIndex();
          return createIndex(parent->children().indexOf(node), 0, node);
      }

      void BookmarksModel::entryAdded(BookmarkNode *item)
      {
          Q_ASSERT(item && item->parent());
          int row = item->parent()->children().indexOf(item);
          BookmarkNode *parent = item->parent();
          // item was already added so remove beore beginInsertRows is called
          parent->remove(item);
          beginInsertRows(index(parent), row, row);
          parent->add(item, row);
          endInsertRows();
      }

      void BookmarksModel::entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item)
      {
          // item was already removed, re-add so beginRemoveRows works
          parent->add(item, row);
          beginRemoveRows(index(parent), row, row);
          parent->remove(item);
          endRemoveRows();
      }

      void BookmarksModel::entryChanged(BookmarkNode *item)
      {
          QModelIndex idx = index(item);
          emit dataChanged(idx, idx);
      }

      bool BookmarksModel::removeRows(int row, int count, const QModelIndex &parent)
      {
          if (row < 0 || count <= 0 || row + count > rowCount(parent))
              return false;

          BookmarkNode *bookmarkNode = node(parent);
          for (int i = row + count - 1; i >= row; --i) {
              BookmarkNode *node = bookmarkNode->children().at(i);
              if (node == m_bookmarksManager->menu()
                  || node == m_bookmarksManager->toolbar())
                  continue;

              m_bookmarksManager->removeBookmark(node);
          }
          if (m_endMacro) {
              m_bookmarksManager->undoRedoStack()->endMacro();
              m_endMacro = false;
          }
          return true;
      }

      QVariant BookmarksModel::headerData(int section, Qt::Orientation orientation, int role) const
      {
          if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
              switch (section) {
                  case 0: return tr("Title");
                  case 1: return tr("Address");
              }
          }
          return QAbstractItemModel::headerData(section, orientation, role);
      }

      QVariant BookmarksModel::data(const QModelIndex &index, int role) const
      {
          if (!index.isValid() || index.model() != this)
              return QVariant();

          const BookmarkNode *bookmarkNode = node(index);
          switch (role) {
          case Qt::EditRole:
          case Qt::DisplayRole:
              if (bookmarkNode->type() == BookmarkNode::Separator) {
                  switch (index.column()) {
                  case 0: return QString(50, 0xB7);
                  case 1: return QString();
                  }
              }

              switch (index.column()) {
              case 0: return bookmarkNode->title;
              case 1: return bookmarkNode->url;
              }
              break;
          case BookmarksModel::UrlRole:
              return QUrl(bookmarkNode->url);
              break;
          case BookmarksModel::UrlStringRole:
              return bookmarkNode->url;
              break;
          case BookmarksModel::TypeRole:
              return bookmarkNode->type();
              break;
          case BookmarksModel::SeparatorRole:
              return (bookmarkNode->type() == BookmarkNode::Separator);
              break;
          case Qt::DecorationRole:
              if (index.column() == 0) {
                  if (bookmarkNode->type() == BookmarkNode::Folder)
                      return QApplication::style()->standardIcon(QStyle::SP_DirIcon);
                  return BrowserApplication::instance()->icon(bookmarkNode->url);
              }
          }

          return QVariant();
      }

      int BookmarksModel::columnCount(const QModelIndex &parent) const
      {
          return (parent.column() > 0) ? 0 : 2;
      }

      int BookmarksModel::rowCount(const QModelIndex &parent) const
      {
          if (parent.column() > 0)
              return 0;

          if (!parent.isValid())
              return m_bookmarksManager->bookmarks()->children().count();

          const BookmarkNode *item = static_cast<BookmarkNode*>(parent.internalPointer());
          return item->children().count();
      }

      QModelIndex BookmarksModel::index(int row, int column, const QModelIndex &parent) const
      {
          if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
              return QModelIndex();

          // get the parent node
          BookmarkNode *parentNode = node(parent);
          return createIndex(row, column, parentNode->children().at(row));
      }

      QModelIndex BookmarksModel::parent(const QModelIndex &index) const
      {
          if (!index.isValid())
              return QModelIndex();

          BookmarkNode *itemNode = node(index);
          BookmarkNode *parentNode = (itemNode ? itemNode->parent() : 0);
          if (!parentNode || parentNode == m_bookmarksManager->bookmarks())
              return QModelIndex();

          // get the parent's row
          BookmarkNode *grandParentNode = parentNode->parent();
          int parentRow = grandParentNode->children().indexOf(parentNode);
          Q_ASSERT(parentRow >= 0);
          return createIndex(parentRow, 0, parentNode);
      }

      bool BookmarksModel::hasChildren(const QModelIndex &parent) const
      {
          if (!parent.isValid())
              return true;
          const BookmarkNode *parentNode = node(parent);
          return (parentNode->type() == BookmarkNode::Folder);
      }

      Qt::ItemFlags BookmarksModel::flags(const QModelIndex &index) const
      {
          if (!index.isValid())
              return Qt::NoItemFlags;

          Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;

          BookmarkNode *bookmarkNode = node(index);

          if (bookmarkNode != m_bookmarksManager->menu()
              && bookmarkNode != m_bookmarksManager->toolbar()) {
              flags |= Qt::ItemIsDragEnabled;
              if (bookmarkNode->type() != BookmarkNode::Separator)
                  flags |= Qt::ItemIsEditable;
          }
          if (hasChildren(index))
              flags |= Qt::ItemIsDropEnabled;
          return flags;
      }

      Qt::DropActions BookmarksModel::supportedDropActions () const
      {
          return Qt::CopyAction | Qt::MoveAction;
      }

      #define MIMETYPE QLatin1String("application/bookmarks.xbel")

      QStringList BookmarksModel::mimeTypes() const
      {
          QStringList types;
          types << MIMETYPE;
          return types;
      }

      QMimeData *BookmarksModel::mimeData(const QModelIndexList &indexes) const
      {
          QMimeData *mimeData = new QMimeData();
          QByteArray data;
          QDataStream stream(&data, QIODevice::WriteOnly);
          foreach (QModelIndex index, indexes) {
              if (index.column() != 0 || !index.isValid())
                  continue;
              QByteArray encodedData;
              QBuffer buffer(&encodedData);
              buffer.open(QBuffer::ReadWrite);
              XbelWriter writer;
              const BookmarkNode *parentNode = node(index);
              writer.write(&buffer, parentNode);
              stream << encodedData;
          }
          mimeData->setData(MIMETYPE, data);
          return mimeData;
      }

      bool BookmarksModel::dropMimeData(const QMimeData *data,
           Qt::DropAction action, int row, int column, const QModelIndex &parent)
      {
          if (action == Qt::IgnoreAction)
              return true;

          if (!data->hasFormat(MIMETYPE)
              || column > 0)
              return false;

          QByteArray ba = data->data(MIMETYPE);
          QDataStream stream(&ba, QIODevice::ReadOnly);
          if (stream.atEnd())
              return false;

          QUndoStack *undoStack = m_bookmarksManager->undoRedoStack();
          undoStack->beginMacro(QLatin1String("Move Bookmarks"));

          while (!stream.atEnd()) {
              QByteArray encodedData;
              stream >> encodedData;
              QBuffer buffer(&encodedData);
              buffer.open(QBuffer::ReadOnly);

              XbelReader reader;
              BookmarkNode *rootNode = reader.read(&buffer);
              QList<BookmarkNode*> children = rootNode->children();
              for (int i = 0; i < children.count(); ++i) {
                  BookmarkNode *bookmarkNode = children.at(i);
                  rootNode->remove(bookmarkNode);
                  row = qMax(0, row);
                  BookmarkNode *parentNode = node(parent);
                  m_bookmarksManager->addBookmark(parentNode, bookmarkNode, row);
                  m_endMacro = true;
              }
              delete rootNode;
          }
          return true;
      }

      bool BookmarksModel::setData(const QModelIndex &index, const QVariant &value, int role)
      {
          if (!index.isValid() || (flags(index) & Qt::ItemIsEditable) == 0)
              return false;

          BookmarkNode *item = node(index);

          switch (role) {
          case Qt::EditRole:
          case Qt::DisplayRole:
              if (index.column() == 0) {
                  m_bookmarksManager->setTitle(item, value.toString());
                  break;
              }
              if (index.column() == 1) {
                  m_bookmarksManager->setUrl(item, value.toString());
                  break;
              }
              return false;
          case BookmarksModel::UrlRole:
              m_bookmarksManager->setUrl(item, value.toUrl().toString());
              break;
          case BookmarksModel::UrlStringRole:
              m_bookmarksManager->setUrl(item, value.toString());
              break;
          default:
              break;
              return false;
          }

          return true;
      }

      BookmarkNode *BookmarksModel::node(const QModelIndex &index) const
      {
          BookmarkNode *itemNode = static_cast<BookmarkNode*>(index.internalPointer());
          if (!itemNode)
              return m_bookmarksManager->bookmarks();
          return itemNode;
      }

      AddBookmarkProxyModel::AddBookmarkProxyModel(QObject *parent)
          : QSortFilterProxyModel(parent)
      {
      }

      int AddBookmarkProxyModel::columnCount(const QModelIndex &parent) const
      {
          return qMin(1, QSortFilterProxyModel::columnCount(parent));
      }

      bool AddBookmarkProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
      {
          QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
          return sourceModel()->hasChildren(idx);
      }

      AddBookmarkDialog::AddBookmarkDialog(const QString &url, const QString &title, QWidget *parent, BookmarksManager *bookmarkManager)
          : QDialog(parent)
          , m_url(url)
          , m_bookmarksManager(bookmarkManager)
      {
          setWindowFlags(Qt::Sheet);
          if (!m_bookmarksManager)
              m_bookmarksManager = BrowserApplication::bookmarksManager();
          setupUi(this);
          QTreeView *view = new QTreeView(this);
          m_proxyModel = new AddBookmarkProxyModel(this);
          BookmarksModel *model = m_bookmarksManager->bookmarksModel();
          m_proxyModel->setSourceModel(model);
          view->setModel(m_proxyModel);
          view->expandAll();
          view->header()->setStretchLastSection(true);
          view->header()->hide();
          view->setItemsExpandable(false);
          view->setRootIsDecorated(false);
          view->setIndentation(10);
          location->setModel(m_proxyModel);
          view->show();
          location->setView(view);
          BookmarkNode *menu = m_bookmarksManager->menu();
          QModelIndex idx = m_proxyModel->mapFromSource(model->index(menu));
          view->setCurrentIndex(idx);
          location->setCurrentIndex(idx.row());
          name->setText(title);
      }

      void AddBookmarkDialog::accept()
      {
          QModelIndex index = location->view()->currentIndex();
          index = m_proxyModel->mapToSource(index);
          if (!index.isValid())
              index = m_bookmarksManager->bookmarksModel()->index(0, 0);
          BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(index);
          BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark);
          bookmark->url = m_url;
          bookmark->title = name->text();
          m_bookmarksManager->addBookmark(parent, bookmark);
          QDialog::accept();
      }

      BookmarksMenu::BookmarksMenu(QWidget *parent)
          : ModelMenu(parent)
          , m_bookmarksManager(0)
      {
          connect(this, SIGNAL(activated(QModelIndex)),
                  this, SLOT(activated(QModelIndex)));
          setMaxRows(-1);
          setHoverRole(BookmarksModel::UrlStringRole);
          setSeparatorRole(BookmarksModel::SeparatorRole);
      }

      void BookmarksMenu::activated(const QModelIndex &index)
      {
          emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
      }

      bool BookmarksMenu::prePopulated()
      {
          m_bookmarksManager = BrowserApplication::bookmarksManager();
          setModel(m_bookmarksManager->bookmarksModel());
          setRootIndex(m_bookmarksManager->bookmarksModel()->index(1, 0));
          // initial actions
          for (int i = 0; i < m_initialActions.count(); ++i)
              addAction(m_initialActions.at(i));
          if (!m_initialActions.isEmpty())
              addSeparator();
          createMenu(model()->index(0, 0), 1, this);
          return true;
      }

      void BookmarksMenu::setInitialActions(QList<QAction*> actions)
      {
          m_initialActions = actions;
          for (int i = 0; i < m_initialActions.count(); ++i)
              addAction(m_initialActions.at(i));
      }

      BookmarksDialog::BookmarksDialog(QWidget *parent, BookmarksManager *manager)
          : QDialog(parent)
      {
          m_bookmarksManager = manager;
          if (!m_bookmarksManager)
              m_bookmarksManager = BrowserApplication::bookmarksManager();
          setupUi(this);

          tree->setUniformRowHeights(true);
          tree->setSelectionBehavior(QAbstractItemView::SelectRows);
          tree->setSelectionMode(QAbstractItemView::ContiguousSelection);
          tree->setTextElideMode(Qt::ElideMiddle);
          m_bookmarksModel = m_bookmarksManager->bookmarksModel();
          m_proxyModel = new TreeProxyModel(this);
          connect(search, SIGNAL(textChanged(QString)),
                  m_proxyModel, SLOT(setFilterFixedString(QString)));
          connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne()));
          m_proxyModel->setSourceModel(m_bookmarksModel);
          tree->setModel(m_proxyModel);
          tree->setDragDropMode(QAbstractItemView::InternalMove);
          tree->setExpanded(m_proxyModel->index(0, 0), true);
          tree->setAlternatingRowColors(true);
          QFontMetrics fm(font());
          int header = fm.width(QLatin1Char('m')) * 40;
          tree->header()->resizeSection(0, header);
          tree->header()->setStretchLastSection(true);
          connect(tree, SIGNAL(activated(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);
      }

      BookmarksDialog::~BookmarksDialog()
      {
          if (saveExpandedNodes(tree->rootIndex()))
              m_bookmarksManager->changeExpanded();
      }

      bool BookmarksDialog::saveExpandedNodes(const QModelIndex &parent)
      {
          bool changed = false;
          for (int i = 0; i < m_proxyModel->rowCount(parent); ++i) {
              QModelIndex child = m_proxyModel->index(i, 0, parent);
              QModelIndex sourceIndex = m_proxyModel->mapToSource(child);
              BookmarkNode *childNode = m_bookmarksModel->node(sourceIndex);
              bool wasExpanded = childNode->expanded;
              if (tree->isExpanded(child)) {
                  childNode->expanded = true;
                  changed |= saveExpandedNodes(child);
              } else {
                  childNode->expanded = false;
              }
              changed |= (wasExpanded != childNode->expanded);
          }
          return changed;
      }

      void BookmarksDialog::expandNodes(BookmarkNode *node)
      {
          for (int i = 0; i < node->children().count(); ++i) {
              BookmarkNode *childNode = node->children()[i];
              if (childNode->expanded) {
                  QModelIndex idx = m_bookmarksModel->index(childNode);
                  idx = m_proxyModel->mapFromSource(idx);
                  tree->setExpanded(idx, true);
                  expandNodes(childNode);
              }
          }
      }

      void BookmarksDialog::customContextMenuRequested(const QPoint &pos)
      {
          QMenu menu;
          QModelIndex index = tree->indexAt(pos);
          index = index.sibling(index.row(), 0);
          if (index.isValid() && !tree->model()->hasChildren(index)) {
              menu.addAction(tr("Open"), this, SLOT(open()));
              menu.addSeparator();
          }
          menu.addAction(tr("Delete"), tree, SLOT(removeOne()));
          menu.exec(QCursor::pos());
      }

      void BookmarksDialog::open()
      {
          QModelIndex index = tree->currentIndex();
          if (!index.parent().isValid())
              return;
          emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel::UrlRole).toUrl());
      }

      void BookmarksDialog::newFolder()
      {
          QModelIndex currentIndex = tree->currentIndex();
          QModelIndex idx = currentIndex;
          if (idx.isValid() && !idx.model()->hasChildren(idx))
              idx = idx.parent();
          if (!idx.isValid())
              idx = tree->rootIndex();
          idx = m_proxyModel->mapToSource(idx);
          BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(idx);
          BookmarkNode *node = new BookmarkNode(BookmarkNode::Folder);
          node->title = tr("New Folder");
          m_bookmarksManager->addBookmark(parent, node, currentIndex.row() + 1);
      }

      BookmarksToolBar::BookmarksToolBar(BookmarksModel *model, QWidget *parent)
          : QToolBar(tr("Bookmark"), parent)
          , m_bookmarksModel(model)
      {
          connect(this, SIGNAL(actionTriggered(QAction*)), this, SLOT(triggered(QAction*)));
          setRootIndex(model->index(0, 0));
          connect(m_bookmarksModel, SIGNAL(modelReset()), this, SLOT(build()));
          connect(m_bookmarksModel, SIGNAL(rowsInserted(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 BookmarksToolBar::dragEnterEvent(QDragEnterEvent *event)
      {
          const QMimeData *mimeData = event->mimeData();
          if (mimeData->hasUrls())
              event->acceptProposedAction();
          QToolBar::dragEnterEvent(event);
      }

      void BookmarksToolBar::dropEvent(QDropEvent *event)
      {
          const QMimeData *mimeData = event->mimeData();
          if (mimeData->hasUrls() && mimeData->hasText()) {
              QList<QUrl> urls = mimeData->urls();
              QAction *action = actionAt(event->pos());
              QString dropText;
              if (action)
                  dropText = action->text();
              int row = -1;
              QModelIndex parentIndex = m_root;
              for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) {
                  QModelIndex idx = m_bookmarksModel->index(i, 0, m_root);
                  QString title = idx.data().toString();
                  if (title == dropText) {
                      row = i;
                      if (m_bookmarksModel->hasChildren(idx)) {
                          parentIndex = idx;
                          row = -1;
                      }
                      break;
                  }
              }
              BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark);
              bookmark->url = urls.at(0).toString();
              bookmark->title = mimeData->text();

              BookmarkNode *parent = m_bookmarksModel->node(parentIndex);
              BookmarksManager *bookmarksManager = m_bookmarksModel->bookmarksManager();
              bookmarksManager->addBookmark(parent, bookmark, row);
              event->acceptProposedAction();
          }
          QToolBar::dropEvent(event);
      }

      void BookmarksToolBar::setRootIndex(const QModelIndex &index)
      {
          m_root = index;
          build();
      }

      QModelIndex BookmarksToolBar::rootIndex() const
      {
          return m_root;
      }

      void BookmarksToolBar::build()
      {
          clear();
          for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) {
              QModelIndex idx = m_bookmarksModel->index(i, 0, m_root);
              if (m_bookmarksModel->hasChildren(idx)) {
                  QToolButton *button = new QToolButton(this);
                  button->setPopupMode(QToolButton::InstantPopup);
                  button->setArrowType(Qt::DownArrow);
                  button->setText(idx.data().toString());
                  ModelMenu *menu = new ModelMenu(this);
                  connect(menu, SIGNAL(activated(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));
              }
          }
      }

      void BookmarksToolBar::triggered(QAction *action)
      {
          QVariant v = action->data();
          if (v.canConvert<QUrl>()) {
              emit openUrl(v.toUrl());
          }
      }

      void BookmarksToolBar::activated(const QModelIndex &index)
      {
          emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
      }