demos/browser/bookmarks.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the demonstration applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "bookmarks.h"
       
    43 
       
    44 #include "autosaver.h"
       
    45 #include "browserapplication.h"
       
    46 #include "history.h"
       
    47 #include "xbel.h"
       
    48 
       
    49 #include <QtCore/QBuffer>
       
    50 #include <QtCore/QFile>
       
    51 #include <QtCore/QMimeData>
       
    52 
       
    53 #include <QtGui/QDesktopServices>
       
    54 #include <QtGui/QDragEnterEvent>
       
    55 #include <QtGui/QFileDialog>
       
    56 #include <QtGui/QHeaderView>
       
    57 #include <QtGui/QIcon>
       
    58 #include <QtGui/QMessageBox>
       
    59 #include <QtGui/QToolButton>
       
    60 
       
    61 #include <QtWebKit/QWebSettings>
       
    62 
       
    63 #include <QtCore/QDebug>
       
    64 
       
    65 #define BOOKMARKBAR "Bookmarks Bar"
       
    66 #define BOOKMARKMENU "Bookmarks Menu"
       
    67 
       
    68 BookmarksManager::BookmarksManager(QObject *parent)
       
    69     : QObject(parent)
       
    70     , m_loaded(false)
       
    71     , m_saveTimer(new AutoSaver(this))
       
    72     , m_bookmarkRootNode(0)
       
    73     , m_bookmarkModel(0)
       
    74 {
       
    75     connect(this, SIGNAL(entryAdded(BookmarkNode *)),
       
    76             m_saveTimer, SLOT(changeOccurred()));
       
    77     connect(this, SIGNAL(entryRemoved(BookmarkNode *, int, BookmarkNode *)),
       
    78             m_saveTimer, SLOT(changeOccurred()));
       
    79     connect(this, SIGNAL(entryChanged(BookmarkNode *)),
       
    80             m_saveTimer, SLOT(changeOccurred()));
       
    81 }
       
    82 
       
    83 BookmarksManager::~BookmarksManager()
       
    84 {
       
    85     m_saveTimer->saveIfNeccessary();
       
    86 }
       
    87 
       
    88 void BookmarksManager::changeExpanded()
       
    89 {
       
    90     m_saveTimer->changeOccurred();
       
    91 }
       
    92 
       
    93 void BookmarksManager::load()
       
    94 {
       
    95     if (m_loaded)
       
    96         return;
       
    97     m_loaded = true;
       
    98 
       
    99     QString dir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
       
   100     QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
       
   101     if (!QFile::exists(bookmarkFile))
       
   102         bookmarkFile = QLatin1String(":defaultbookmarks.xbel");
       
   103 
       
   104     XbelReader reader;
       
   105     m_bookmarkRootNode = reader.read(bookmarkFile);
       
   106     if (reader.error() != QXmlStreamReader::NoError) {
       
   107         QMessageBox::warning(0, QLatin1String("Loading Bookmark"),
       
   108             tr("Error when loading bookmarks on line %1, column %2:\n"
       
   109                "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
       
   110     }
       
   111 
       
   112     BookmarkNode *toolbar = 0;
       
   113     BookmarkNode *menu = 0;
       
   114     QList<BookmarkNode*> others;
       
   115     for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
       
   116         BookmarkNode *node = m_bookmarkRootNode->children().at(i);
       
   117         if (node->type() == BookmarkNode::Folder) {
       
   118             // Automatically convert
       
   119             if (node->title == tr("Toolbar Bookmarks") && !toolbar) {
       
   120                 node->title = tr(BOOKMARKBAR);
       
   121             }
       
   122             if (node->title == tr(BOOKMARKBAR) && !toolbar) {
       
   123                 toolbar = node;
       
   124             }
       
   125 
       
   126             // Automatically convert
       
   127             if (node->title == tr("Menu") && !menu) {
       
   128                 node->title = tr(BOOKMARKMENU);
       
   129             }
       
   130             if (node->title == tr(BOOKMARKMENU) && !menu) {
       
   131                 menu = node;
       
   132             }
       
   133         } else {
       
   134             others.append(node);
       
   135         }
       
   136         m_bookmarkRootNode->remove(node);
       
   137     }
       
   138     Q_ASSERT(m_bookmarkRootNode->children().count() == 0);
       
   139     if (!toolbar) {
       
   140         toolbar = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode);
       
   141         toolbar->title = tr(BOOKMARKBAR);
       
   142     } else {
       
   143         m_bookmarkRootNode->add(toolbar);
       
   144     }
       
   145 
       
   146     if (!menu) {
       
   147         menu = new BookmarkNode(BookmarkNode::Folder, m_bookmarkRootNode);
       
   148         menu->title = tr(BOOKMARKMENU);
       
   149     } else {
       
   150         m_bookmarkRootNode->add(menu);
       
   151     }
       
   152 
       
   153     for (int i = 0; i < others.count(); ++i)
       
   154         menu->add(others.at(i));
       
   155 }
       
   156 
       
   157 void BookmarksManager::save() const
       
   158 {
       
   159     if (!m_loaded)
       
   160         return;
       
   161 
       
   162     XbelWriter writer;
       
   163     QString dir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
       
   164     QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
       
   165     if (!writer.write(bookmarkFile, m_bookmarkRootNode))
       
   166         qWarning() << "BookmarkManager: error saving to" << bookmarkFile;
       
   167 }
       
   168 
       
   169 void BookmarksManager::addBookmark(BookmarkNode *parent, BookmarkNode *node, int row)
       
   170 {
       
   171     if (!m_loaded)
       
   172         return;
       
   173     Q_ASSERT(parent);
       
   174     InsertBookmarksCommand *command = new InsertBookmarksCommand(this, parent, node, row);
       
   175     m_commands.push(command);
       
   176 }
       
   177 
       
   178 void BookmarksManager::removeBookmark(BookmarkNode *node)
       
   179 {
       
   180     if (!m_loaded)
       
   181         return;
       
   182 
       
   183     Q_ASSERT(node);
       
   184     BookmarkNode *parent = node->parent();
       
   185     int row = parent->children().indexOf(node);
       
   186     RemoveBookmarksCommand *command = new RemoveBookmarksCommand(this, parent, row);
       
   187     m_commands.push(command);
       
   188 }
       
   189 
       
   190 void BookmarksManager::setTitle(BookmarkNode *node, const QString &newTitle)
       
   191 {
       
   192     if (!m_loaded)
       
   193         return;
       
   194 
       
   195     Q_ASSERT(node);
       
   196     ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newTitle, true);
       
   197     m_commands.push(command);
       
   198 }
       
   199 
       
   200 void BookmarksManager::setUrl(BookmarkNode *node, const QString &newUrl)
       
   201 {
       
   202     if (!m_loaded)
       
   203         return;
       
   204 
       
   205     Q_ASSERT(node);
       
   206     ChangeBookmarkCommand *command = new ChangeBookmarkCommand(this, node, newUrl, false);
       
   207     m_commands.push(command);
       
   208 }
       
   209 
       
   210 BookmarkNode *BookmarksManager::bookmarks()
       
   211 {
       
   212     if (!m_loaded)
       
   213         load();
       
   214     return m_bookmarkRootNode;
       
   215 }
       
   216 
       
   217 BookmarkNode *BookmarksManager::menu()
       
   218 {
       
   219     if (!m_loaded)
       
   220         load();
       
   221 
       
   222     for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
       
   223         BookmarkNode *node = m_bookmarkRootNode->children().at(i);
       
   224         if (node->title == tr(BOOKMARKMENU))
       
   225             return node;
       
   226     }
       
   227     Q_ASSERT(false);
       
   228     return 0;
       
   229 }
       
   230 
       
   231 BookmarkNode *BookmarksManager::toolbar()
       
   232 {
       
   233     if (!m_loaded)
       
   234         load();
       
   235 
       
   236     for (int i = m_bookmarkRootNode->children().count() - 1; i >= 0; --i) {
       
   237         BookmarkNode *node = m_bookmarkRootNode->children().at(i);
       
   238         if (node->title == tr(BOOKMARKBAR))
       
   239             return node;
       
   240     }
       
   241     Q_ASSERT(false);
       
   242     return 0;
       
   243 }
       
   244 
       
   245 BookmarksModel *BookmarksManager::bookmarksModel()
       
   246 {
       
   247     if (!m_bookmarkModel)
       
   248         m_bookmarkModel = new BookmarksModel(this, this);
       
   249     return m_bookmarkModel;
       
   250 }
       
   251 
       
   252 void BookmarksManager::importBookmarks()
       
   253 {
       
   254     QString fileName = QFileDialog::getOpenFileName(0, tr("Open File"),
       
   255                                                      QString(),
       
   256                                                      tr("XBEL (*.xbel *.xml)"));
       
   257     if (fileName.isEmpty())
       
   258         return;
       
   259 
       
   260     XbelReader reader;
       
   261     BookmarkNode *importRootNode = reader.read(fileName);
       
   262     if (reader.error() != QXmlStreamReader::NoError) {
       
   263         QMessageBox::warning(0, QLatin1String("Loading Bookmark"),
       
   264             tr("Error when loading bookmarks on line %1, column %2:\n"
       
   265                "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
       
   266     }
       
   267 
       
   268     importRootNode->setType(BookmarkNode::Folder);
       
   269     importRootNode->title = (tr("Imported %1").arg(QDate::currentDate().toString(Qt::SystemLocaleShortDate)));
       
   270     addBookmark(menu(), importRootNode);
       
   271 }
       
   272 
       
   273 void BookmarksManager::exportBookmarks()
       
   274 {
       
   275     QString fileName = QFileDialog::getSaveFileName(0, tr("Save File"),
       
   276                                 tr("%1 Bookmarks.xbel").arg(QCoreApplication::applicationName()),
       
   277                                 tr("XBEL (*.xbel *.xml)"));
       
   278     if (fileName.isEmpty())
       
   279         return;
       
   280 
       
   281     XbelWriter writer;
       
   282     if (!writer.write(fileName, m_bookmarkRootNode))
       
   283         QMessageBox::critical(0, tr("Export error"), tr("error saving bookmarks"));
       
   284 }
       
   285 
       
   286 RemoveBookmarksCommand::RemoveBookmarksCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *parent, int row)
       
   287     : QUndoCommand(BookmarksManager::tr("Remove Bookmark"))
       
   288     , m_row(row)
       
   289     , m_bookmarkManagaer(m_bookmarkManagaer)
       
   290     , m_node(parent->children().value(row))
       
   291     , m_parent(parent)
       
   292     , m_done(false)
       
   293 {
       
   294 }
       
   295 
       
   296 RemoveBookmarksCommand::~RemoveBookmarksCommand()
       
   297 {
       
   298     if (m_done && !m_node->parent()) {
       
   299         delete m_node;
       
   300     }
       
   301 }
       
   302 
       
   303 void RemoveBookmarksCommand::undo()
       
   304 {
       
   305     m_parent->add(m_node, m_row);
       
   306     emit m_bookmarkManagaer->entryAdded(m_node);
       
   307     m_done = false;
       
   308 }
       
   309 
       
   310 void RemoveBookmarksCommand::redo()
       
   311 {
       
   312     m_parent->remove(m_node);
       
   313     emit m_bookmarkManagaer->entryRemoved(m_parent, m_row, m_node);
       
   314     m_done = true;
       
   315 }
       
   316 
       
   317 InsertBookmarksCommand::InsertBookmarksCommand(BookmarksManager *m_bookmarkManagaer,
       
   318                 BookmarkNode *parent, BookmarkNode *node, int row)
       
   319     : RemoveBookmarksCommand(m_bookmarkManagaer, parent, row)
       
   320 {
       
   321     setText(BookmarksManager::tr("Insert Bookmark"));
       
   322     m_node = node;
       
   323 }
       
   324 
       
   325 ChangeBookmarkCommand::ChangeBookmarkCommand(BookmarksManager *m_bookmarkManagaer, BookmarkNode *node,
       
   326                         const QString &newValue, bool title)
       
   327     : QUndoCommand()
       
   328     , m_bookmarkManagaer(m_bookmarkManagaer)
       
   329     , m_title(title)
       
   330     , m_newValue(newValue)
       
   331     , m_node(node)
       
   332 {
       
   333     if (m_title) {
       
   334         m_oldValue = m_node->title;
       
   335         setText(BookmarksManager::tr("Name Change"));
       
   336     } else {
       
   337         m_oldValue = m_node->url;
       
   338         setText(BookmarksManager::tr("Address Change"));
       
   339     }
       
   340 }
       
   341 
       
   342 void ChangeBookmarkCommand::undo()
       
   343 {
       
   344     if (m_title)
       
   345         m_node->title = m_oldValue;
       
   346     else
       
   347         m_node->url = m_oldValue;
       
   348     emit m_bookmarkManagaer->entryChanged(m_node);
       
   349 }
       
   350 
       
   351 void ChangeBookmarkCommand::redo()
       
   352 {
       
   353     if (m_title)
       
   354         m_node->title = m_newValue;
       
   355     else
       
   356         m_node->url = m_newValue;
       
   357     emit m_bookmarkManagaer->entryChanged(m_node);
       
   358 }
       
   359 
       
   360 BookmarksModel::BookmarksModel(BookmarksManager *bookmarkManager, QObject *parent)
       
   361     : QAbstractItemModel(parent)
       
   362     , m_endMacro(false)
       
   363     , m_bookmarksManager(bookmarkManager)
       
   364 {
       
   365     connect(bookmarkManager, SIGNAL(entryAdded(BookmarkNode *)),
       
   366             this, SLOT(entryAdded(BookmarkNode *)));
       
   367     connect(bookmarkManager, SIGNAL(entryRemoved(BookmarkNode *, int, BookmarkNode *)),
       
   368             this, SLOT(entryRemoved(BookmarkNode *, int, BookmarkNode *)));
       
   369     connect(bookmarkManager, SIGNAL(entryChanged(BookmarkNode *)),
       
   370             this, SLOT(entryChanged(BookmarkNode *)));
       
   371 }
       
   372 
       
   373 QModelIndex BookmarksModel::index(BookmarkNode *node) const
       
   374 {
       
   375     BookmarkNode *parent = node->parent();
       
   376     if (!parent)
       
   377         return QModelIndex();
       
   378     return createIndex(parent->children().indexOf(node), 0, node);
       
   379 }
       
   380 
       
   381 void BookmarksModel::entryAdded(BookmarkNode *item)
       
   382 {
       
   383     Q_ASSERT(item && item->parent());
       
   384     int row = item->parent()->children().indexOf(item);
       
   385     BookmarkNode *parent = item->parent();
       
   386     // item was already added so remove beore beginInsertRows is called
       
   387     parent->remove(item);
       
   388     beginInsertRows(index(parent), row, row);
       
   389     parent->add(item, row);
       
   390     endInsertRows();
       
   391 }
       
   392 
       
   393 void BookmarksModel::entryRemoved(BookmarkNode *parent, int row, BookmarkNode *item)
       
   394 {
       
   395     // item was already removed, re-add so beginRemoveRows works
       
   396     parent->add(item, row);
       
   397     beginRemoveRows(index(parent), row, row);
       
   398     parent->remove(item);
       
   399     endRemoveRows();
       
   400 }
       
   401 
       
   402 void BookmarksModel::entryChanged(BookmarkNode *item)
       
   403 {
       
   404     QModelIndex idx = index(item);
       
   405     emit dataChanged(idx, idx);
       
   406 }
       
   407 
       
   408 bool BookmarksModel::removeRows(int row, int count, const QModelIndex &parent)
       
   409 {
       
   410     if (row < 0 || count <= 0 || row + count > rowCount(parent))
       
   411         return false;
       
   412 
       
   413     BookmarkNode *bookmarkNode = node(parent);
       
   414     for (int i = row + count - 1; i >= row; --i) {
       
   415         BookmarkNode *node = bookmarkNode->children().at(i);
       
   416         if (node == m_bookmarksManager->menu()
       
   417             || node == m_bookmarksManager->toolbar())
       
   418             continue;
       
   419 
       
   420         m_bookmarksManager->removeBookmark(node);
       
   421     }
       
   422     if (m_endMacro) {
       
   423         m_bookmarksManager->undoRedoStack()->endMacro();
       
   424         m_endMacro = false;
       
   425     }
       
   426     return true;
       
   427 }
       
   428 
       
   429 QVariant BookmarksModel::headerData(int section, Qt::Orientation orientation, int role) const
       
   430 {
       
   431     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
       
   432         switch (section) {
       
   433             case 0: return tr("Title");
       
   434             case 1: return tr("Address");
       
   435         }
       
   436     }
       
   437     return QAbstractItemModel::headerData(section, orientation, role);
       
   438 }
       
   439 
       
   440 QVariant BookmarksModel::data(const QModelIndex &index, int role) const
       
   441 {
       
   442     if (!index.isValid() || index.model() != this)
       
   443         return QVariant();
       
   444 
       
   445     const BookmarkNode *bookmarkNode = node(index);
       
   446     switch (role) {
       
   447     case Qt::EditRole:
       
   448     case Qt::DisplayRole:
       
   449         if (bookmarkNode->type() == BookmarkNode::Separator) {
       
   450             switch (index.column()) {
       
   451             case 0: return QString(50, 0xB7);
       
   452             case 1: return QString();
       
   453             }
       
   454         }
       
   455 
       
   456         switch (index.column()) {
       
   457         case 0: return bookmarkNode->title;
       
   458         case 1: return bookmarkNode->url;
       
   459         }
       
   460         break;
       
   461     case BookmarksModel::UrlRole:
       
   462         return QUrl(bookmarkNode->url);
       
   463         break;
       
   464     case BookmarksModel::UrlStringRole:
       
   465         return bookmarkNode->url;
       
   466         break;
       
   467     case BookmarksModel::TypeRole:
       
   468         return bookmarkNode->type();
       
   469         break;
       
   470     case BookmarksModel::SeparatorRole:
       
   471         return (bookmarkNode->type() == BookmarkNode::Separator);
       
   472         break;
       
   473     case Qt::DecorationRole:
       
   474         if (index.column() == 0) {
       
   475             if (bookmarkNode->type() == BookmarkNode::Folder)
       
   476                 return QApplication::style()->standardIcon(QStyle::SP_DirIcon);
       
   477             return BrowserApplication::instance()->icon(bookmarkNode->url);
       
   478         }
       
   479     }
       
   480 
       
   481     return QVariant();
       
   482 }
       
   483 
       
   484 int BookmarksModel::columnCount(const QModelIndex &parent) const
       
   485 {
       
   486     return (parent.column() > 0) ? 0 : 2;
       
   487 }
       
   488 
       
   489 int BookmarksModel::rowCount(const QModelIndex &parent) const
       
   490 {
       
   491     if (parent.column() > 0)
       
   492         return 0;
       
   493 
       
   494     if (!parent.isValid())
       
   495         return m_bookmarksManager->bookmarks()->children().count();
       
   496 
       
   497     const BookmarkNode *item = static_cast<BookmarkNode*>(parent.internalPointer());
       
   498     return item->children().count();
       
   499 }
       
   500 
       
   501 QModelIndex BookmarksModel::index(int row, int column, const QModelIndex &parent) const
       
   502 {
       
   503     if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
       
   504         return QModelIndex();
       
   505 
       
   506     // get the parent node
       
   507     BookmarkNode *parentNode = node(parent);
       
   508     return createIndex(row, column, parentNode->children().at(row));
       
   509 }
       
   510 
       
   511 QModelIndex BookmarksModel::parent(const QModelIndex &index) const
       
   512 {
       
   513     if (!index.isValid())
       
   514         return QModelIndex();
       
   515 
       
   516     BookmarkNode *itemNode = node(index);
       
   517     BookmarkNode *parentNode = (itemNode ? itemNode->parent() : 0);
       
   518     if (!parentNode || parentNode == m_bookmarksManager->bookmarks())
       
   519         return QModelIndex();
       
   520 
       
   521     // get the parent's row
       
   522     BookmarkNode *grandParentNode = parentNode->parent();
       
   523     int parentRow = grandParentNode->children().indexOf(parentNode);
       
   524     Q_ASSERT(parentRow >= 0);
       
   525     return createIndex(parentRow, 0, parentNode);
       
   526 }
       
   527 
       
   528 bool BookmarksModel::hasChildren(const QModelIndex &parent) const
       
   529 {
       
   530     if (!parent.isValid())
       
   531         return true;
       
   532     const BookmarkNode *parentNode = node(parent);
       
   533     return (parentNode->type() == BookmarkNode::Folder);
       
   534 }
       
   535 
       
   536 Qt::ItemFlags BookmarksModel::flags(const QModelIndex &index) const
       
   537 {
       
   538     if (!index.isValid())
       
   539         return Qt::NoItemFlags;
       
   540 
       
   541     Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
       
   542 
       
   543     BookmarkNode *bookmarkNode = node(index);
       
   544 
       
   545     if (bookmarkNode != m_bookmarksManager->menu()
       
   546         && bookmarkNode != m_bookmarksManager->toolbar()) {
       
   547         flags |= Qt::ItemIsDragEnabled;
       
   548         if (bookmarkNode->type() != BookmarkNode::Separator)
       
   549             flags |= Qt::ItemIsEditable;
       
   550     }
       
   551     if (hasChildren(index))
       
   552         flags |= Qt::ItemIsDropEnabled;
       
   553     return flags;
       
   554 }
       
   555 
       
   556 Qt::DropActions BookmarksModel::supportedDropActions () const
       
   557 {
       
   558     return Qt::CopyAction | Qt::MoveAction;
       
   559 }
       
   560 
       
   561 #define MIMETYPE QLatin1String("application/bookmarks.xbel")
       
   562 
       
   563 QStringList BookmarksModel::mimeTypes() const
       
   564 {
       
   565     QStringList types;
       
   566     types << MIMETYPE;
       
   567     return types;
       
   568 }
       
   569 
       
   570 QMimeData *BookmarksModel::mimeData(const QModelIndexList &indexes) const
       
   571 {
       
   572     QMimeData *mimeData = new QMimeData();
       
   573     QByteArray data;
       
   574     QDataStream stream(&data, QIODevice::WriteOnly);
       
   575     foreach (QModelIndex index, indexes) {
       
   576         if (index.column() != 0 || !index.isValid())
       
   577             continue;
       
   578         QByteArray encodedData;
       
   579         QBuffer buffer(&encodedData);
       
   580         buffer.open(QBuffer::ReadWrite);
       
   581         XbelWriter writer;
       
   582         const BookmarkNode *parentNode = node(index);
       
   583         writer.write(&buffer, parentNode);
       
   584         stream << encodedData;
       
   585     }
       
   586     mimeData->setData(MIMETYPE, data);
       
   587     return mimeData;
       
   588 }
       
   589 
       
   590 bool BookmarksModel::dropMimeData(const QMimeData *data,
       
   591      Qt::DropAction action, int row, int column, const QModelIndex &parent)
       
   592 {
       
   593     if (action == Qt::IgnoreAction)
       
   594         return true;
       
   595 
       
   596     if (!data->hasFormat(MIMETYPE)
       
   597         || column > 0)
       
   598         return false;
       
   599 
       
   600     QByteArray ba = data->data(MIMETYPE);
       
   601     QDataStream stream(&ba, QIODevice::ReadOnly);
       
   602     if (stream.atEnd())
       
   603         return false;
       
   604 
       
   605     QUndoStack *undoStack = m_bookmarksManager->undoRedoStack();
       
   606     undoStack->beginMacro(QLatin1String("Move Bookmarks"));
       
   607 
       
   608     while (!stream.atEnd()) {
       
   609         QByteArray encodedData;
       
   610         stream >> encodedData;
       
   611         QBuffer buffer(&encodedData);
       
   612         buffer.open(QBuffer::ReadOnly);
       
   613 
       
   614         XbelReader reader;
       
   615         BookmarkNode *rootNode = reader.read(&buffer);
       
   616         QList<BookmarkNode*> children = rootNode->children();
       
   617         for (int i = 0; i < children.count(); ++i) {
       
   618             BookmarkNode *bookmarkNode = children.at(i);
       
   619             rootNode->remove(bookmarkNode);
       
   620             row = qMax(0, row);
       
   621             BookmarkNode *parentNode = node(parent);
       
   622             m_bookmarksManager->addBookmark(parentNode, bookmarkNode, row);
       
   623             m_endMacro = true;
       
   624         }
       
   625         delete rootNode;
       
   626     }
       
   627     return true;
       
   628 }
       
   629 
       
   630 bool BookmarksModel::setData(const QModelIndex &index, const QVariant &value, int role)
       
   631 {
       
   632     if (!index.isValid() || (flags(index) & Qt::ItemIsEditable) == 0)
       
   633         return false;
       
   634 
       
   635     BookmarkNode *item = node(index);
       
   636 
       
   637     switch (role) {
       
   638     case Qt::EditRole:
       
   639     case Qt::DisplayRole:
       
   640         if (index.column() == 0) {
       
   641             m_bookmarksManager->setTitle(item, value.toString());
       
   642             break;
       
   643         }
       
   644         if (index.column() == 1) {
       
   645             m_bookmarksManager->setUrl(item, value.toString());
       
   646             break;
       
   647         }
       
   648         return false;
       
   649     case BookmarksModel::UrlRole:
       
   650         m_bookmarksManager->setUrl(item, value.toUrl().toString());
       
   651         break;
       
   652     case BookmarksModel::UrlStringRole:
       
   653         m_bookmarksManager->setUrl(item, value.toString());
       
   654         break;
       
   655     default:
       
   656         break;
       
   657         return false;
       
   658     }
       
   659 
       
   660     return true;
       
   661 }
       
   662 
       
   663 BookmarkNode *BookmarksModel::node(const QModelIndex &index) const
       
   664 {
       
   665     BookmarkNode *itemNode = static_cast<BookmarkNode*>(index.internalPointer());
       
   666     if (!itemNode)
       
   667         return m_bookmarksManager->bookmarks();
       
   668     return itemNode;
       
   669 }
       
   670 
       
   671 
       
   672 AddBookmarkProxyModel::AddBookmarkProxyModel(QObject *parent)
       
   673     : QSortFilterProxyModel(parent)
       
   674 {
       
   675 }
       
   676 
       
   677 int AddBookmarkProxyModel::columnCount(const QModelIndex &parent) const
       
   678 {
       
   679     return qMin(1, QSortFilterProxyModel::columnCount(parent));
       
   680 }
       
   681 
       
   682 bool AddBookmarkProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
       
   683 {
       
   684     QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
       
   685     return sourceModel()->hasChildren(idx);
       
   686 }
       
   687 
       
   688 AddBookmarkDialog::AddBookmarkDialog(const QString &url, const QString &title, QWidget *parent, BookmarksManager *bookmarkManager)
       
   689     : QDialog(parent)
       
   690     , m_url(url)
       
   691     , m_bookmarksManager(bookmarkManager)
       
   692 {
       
   693     setWindowFlags(Qt::Sheet);
       
   694     if (!m_bookmarksManager)
       
   695         m_bookmarksManager = BrowserApplication::bookmarksManager();
       
   696     setupUi(this);
       
   697     QTreeView *view = new QTreeView(this);
       
   698     m_proxyModel = new AddBookmarkProxyModel(this);
       
   699     BookmarksModel *model = m_bookmarksManager->bookmarksModel();
       
   700     m_proxyModel->setSourceModel(model);
       
   701     view->setModel(m_proxyModel);
       
   702     view->expandAll();
       
   703     view->header()->setStretchLastSection(true);
       
   704     view->header()->hide();
       
   705     view->setItemsExpandable(false);
       
   706     view->setRootIsDecorated(false);
       
   707     view->setIndentation(10);
       
   708     location->setModel(m_proxyModel);
       
   709     view->show();
       
   710     location->setView(view);
       
   711     BookmarkNode *menu = m_bookmarksManager->menu();
       
   712     QModelIndex idx = m_proxyModel->mapFromSource(model->index(menu));
       
   713     view->setCurrentIndex(idx);
       
   714     location->setCurrentIndex(idx.row());
       
   715     name->setText(title);
       
   716 }
       
   717 
       
   718 void AddBookmarkDialog::accept()
       
   719 {
       
   720     QModelIndex index = location->view()->currentIndex();
       
   721     index = m_proxyModel->mapToSource(index);
       
   722     if (!index.isValid())
       
   723         index = m_bookmarksManager->bookmarksModel()->index(0, 0);
       
   724     BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(index);
       
   725     BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark);
       
   726     bookmark->url = m_url;
       
   727     bookmark->title = name->text();
       
   728     m_bookmarksManager->addBookmark(parent, bookmark);
       
   729     QDialog::accept();
       
   730 }
       
   731 
       
   732 BookmarksMenu::BookmarksMenu(QWidget *parent)
       
   733     : ModelMenu(parent)
       
   734     , m_bookmarksManager(0)
       
   735 {
       
   736     connect(this, SIGNAL(activated(const QModelIndex &)),
       
   737             this, SLOT(activated(const QModelIndex &)));
       
   738     setMaxRows(-1);
       
   739     setHoverRole(BookmarksModel::UrlStringRole);
       
   740     setSeparatorRole(BookmarksModel::SeparatorRole);
       
   741 }
       
   742 
       
   743 void BookmarksMenu::activated(const QModelIndex &index)
       
   744 {
       
   745     emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
       
   746 }
       
   747 
       
   748 bool BookmarksMenu::prePopulated()
       
   749 {
       
   750     m_bookmarksManager = BrowserApplication::bookmarksManager();
       
   751     setModel(m_bookmarksManager->bookmarksModel());
       
   752     setRootIndex(m_bookmarksManager->bookmarksModel()->index(1, 0));
       
   753     // initial actions
       
   754     for (int i = 0; i < m_initialActions.count(); ++i)
       
   755         addAction(m_initialActions.at(i));
       
   756     if (!m_initialActions.isEmpty())
       
   757         addSeparator();
       
   758     createMenu(model()->index(0, 0), 1, this);
       
   759     return true;
       
   760 }
       
   761 
       
   762 void BookmarksMenu::setInitialActions(QList<QAction*> actions)
       
   763 {
       
   764     m_initialActions = actions;
       
   765     for (int i = 0; i < m_initialActions.count(); ++i)
       
   766         addAction(m_initialActions.at(i));
       
   767 }
       
   768 
       
   769 BookmarksDialog::BookmarksDialog(QWidget *parent, BookmarksManager *manager)
       
   770     : QDialog(parent)
       
   771 {
       
   772     m_bookmarksManager = manager;
       
   773     if (!m_bookmarksManager)
       
   774         m_bookmarksManager = BrowserApplication::bookmarksManager();
       
   775     setupUi(this);
       
   776 
       
   777     tree->setUniformRowHeights(true);
       
   778     tree->setSelectionBehavior(QAbstractItemView::SelectRows);
       
   779     tree->setSelectionMode(QAbstractItemView::ContiguousSelection);
       
   780     tree->setTextElideMode(Qt::ElideMiddle);
       
   781     m_bookmarksModel = m_bookmarksManager->bookmarksModel();
       
   782     m_proxyModel = new TreeProxyModel(this);
       
   783     connect(search, SIGNAL(textChanged(QString)),
       
   784             m_proxyModel, SLOT(setFilterFixedString(QString)));
       
   785     connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne()));
       
   786     m_proxyModel->setSourceModel(m_bookmarksModel);
       
   787     tree->setModel(m_proxyModel);
       
   788     tree->setDragDropMode(QAbstractItemView::InternalMove);
       
   789     tree->setExpanded(m_proxyModel->index(0, 0), true);
       
   790     tree->setAlternatingRowColors(true);
       
   791     QFontMetrics fm(font());
       
   792     int header = fm.width(QLatin1Char('m')) * 40;
       
   793     tree->header()->resizeSection(0, header);
       
   794     tree->header()->setStretchLastSection(true);
       
   795     connect(tree, SIGNAL(activated(const QModelIndex&)),
       
   796             this, SLOT(open()));
       
   797     tree->setContextMenuPolicy(Qt::CustomContextMenu);
       
   798     connect(tree, SIGNAL(customContextMenuRequested(const QPoint &)),
       
   799             this, SLOT(customContextMenuRequested(const QPoint &)));
       
   800     connect(addFolderButton, SIGNAL(clicked()),
       
   801             this, SLOT(newFolder()));
       
   802     expandNodes(m_bookmarksManager->bookmarks());
       
   803     setAttribute(Qt::WA_DeleteOnClose);
       
   804 }
       
   805 
       
   806 BookmarksDialog::~BookmarksDialog()
       
   807 {
       
   808     if (saveExpandedNodes(tree->rootIndex()))
       
   809         m_bookmarksManager->changeExpanded();
       
   810 }
       
   811 
       
   812 bool BookmarksDialog::saveExpandedNodes(const QModelIndex &parent)
       
   813 {
       
   814     bool changed = false;
       
   815     for (int i = 0; i < m_proxyModel->rowCount(parent); ++i) {
       
   816         QModelIndex child = m_proxyModel->index(i, 0, parent);
       
   817         QModelIndex sourceIndex = m_proxyModel->mapToSource(child);
       
   818         BookmarkNode *childNode = m_bookmarksModel->node(sourceIndex);
       
   819         bool wasExpanded = childNode->expanded;
       
   820         if (tree->isExpanded(child)) {
       
   821             childNode->expanded = true;
       
   822             changed |= saveExpandedNodes(child);
       
   823         } else {
       
   824             childNode->expanded = false;
       
   825         }
       
   826         changed |= (wasExpanded != childNode->expanded);
       
   827     }
       
   828     return changed;
       
   829 }
       
   830 
       
   831 void BookmarksDialog::expandNodes(BookmarkNode *node)
       
   832 {
       
   833     for (int i = 0; i < node->children().count(); ++i) {
       
   834         BookmarkNode *childNode = node->children()[i];
       
   835         if (childNode->expanded) {
       
   836             QModelIndex idx = m_bookmarksModel->index(childNode);
       
   837             idx = m_proxyModel->mapFromSource(idx);
       
   838             tree->setExpanded(idx, true);
       
   839             expandNodes(childNode);
       
   840         }
       
   841     }
       
   842 }
       
   843 
       
   844 void BookmarksDialog::customContextMenuRequested(const QPoint &pos)
       
   845 {
       
   846     QMenu menu;
       
   847     QModelIndex index = tree->indexAt(pos);
       
   848     index = index.sibling(index.row(), 0);
       
   849     if (index.isValid() && !tree->model()->hasChildren(index)) {
       
   850         menu.addAction(tr("Open"), this, SLOT(open()));
       
   851         menu.addSeparator();
       
   852     }
       
   853     menu.addAction(tr("Delete"), tree, SLOT(removeOne()));
       
   854     menu.exec(QCursor::pos());
       
   855 }
       
   856 
       
   857 void BookmarksDialog::open()
       
   858 {
       
   859     QModelIndex index = tree->currentIndex();
       
   860     if (!index.parent().isValid())
       
   861         return;
       
   862     emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel::UrlRole).toUrl());
       
   863 }
       
   864 
       
   865 void BookmarksDialog::newFolder()
       
   866 {
       
   867     QModelIndex currentIndex = tree->currentIndex();
       
   868     QModelIndex idx = currentIndex;
       
   869     if (idx.isValid() && !idx.model()->hasChildren(idx))
       
   870         idx = idx.parent();
       
   871     if (!idx.isValid())
       
   872         idx = tree->rootIndex();
       
   873     idx = m_proxyModel->mapToSource(idx);
       
   874     BookmarkNode *parent = m_bookmarksManager->bookmarksModel()->node(idx);
       
   875     BookmarkNode *node = new BookmarkNode(BookmarkNode::Folder);
       
   876     node->title = tr("New Folder");
       
   877     m_bookmarksManager->addBookmark(parent, node, currentIndex.row() + 1);
       
   878 }
       
   879 
       
   880 BookmarksToolBar::BookmarksToolBar(BookmarksModel *model, QWidget *parent)
       
   881     : QToolBar(tr("Bookmark"), parent)
       
   882     , m_bookmarksModel(model)
       
   883 {
       
   884     connect(this, SIGNAL(actionTriggered(QAction*)), this, SLOT(triggered(QAction*)));
       
   885     setRootIndex(model->index(0, 0));
       
   886     connect(m_bookmarksModel, SIGNAL(modelReset()), this, SLOT(build()));
       
   887     connect(m_bookmarksModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(build()));
       
   888     connect(m_bookmarksModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(build()));
       
   889     connect(m_bookmarksModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(build()));
       
   890     setAcceptDrops(true);
       
   891 }
       
   892 
       
   893 void BookmarksToolBar::dragEnterEvent(QDragEnterEvent *event)
       
   894 {
       
   895     const QMimeData *mimeData = event->mimeData();
       
   896     if (mimeData->hasUrls())
       
   897         event->acceptProposedAction();
       
   898     QToolBar::dragEnterEvent(event);
       
   899 }
       
   900 
       
   901 void BookmarksToolBar::dropEvent(QDropEvent *event)
       
   902 {
       
   903     const QMimeData *mimeData = event->mimeData();
       
   904     if (mimeData->hasUrls() && mimeData->hasText()) {
       
   905         QList<QUrl> urls = mimeData->urls();
       
   906         QAction *action = actionAt(event->pos());
       
   907         QString dropText;
       
   908         if (action)
       
   909             dropText = action->text();
       
   910         int row = -1;
       
   911         QModelIndex parentIndex = m_root;
       
   912         for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) {
       
   913             QModelIndex idx = m_bookmarksModel->index(i, 0, m_root);
       
   914             QString title = idx.data().toString();
       
   915             if (title == dropText) {
       
   916                 row = i;
       
   917                 if (m_bookmarksModel->hasChildren(idx)) {
       
   918                     parentIndex = idx;
       
   919                     row = -1;
       
   920                 }
       
   921                 break;
       
   922             }
       
   923         }
       
   924         BookmarkNode *bookmark = new BookmarkNode(BookmarkNode::Bookmark);
       
   925         bookmark->url = urls.at(0).toString();
       
   926         bookmark->title = mimeData->text();
       
   927 
       
   928         BookmarkNode *parent = m_bookmarksModel->node(parentIndex);
       
   929         BookmarksManager *bookmarksManager = m_bookmarksModel->bookmarksManager();
       
   930         bookmarksManager->addBookmark(parent, bookmark, row);
       
   931         event->acceptProposedAction();
       
   932     }
       
   933     QToolBar::dropEvent(event);
       
   934 }
       
   935 
       
   936 
       
   937 void BookmarksToolBar::setRootIndex(const QModelIndex &index)
       
   938 {
       
   939     m_root = index;
       
   940     build();
       
   941 }
       
   942 
       
   943 QModelIndex BookmarksToolBar::rootIndex() const
       
   944 {
       
   945     return m_root;
       
   946 }
       
   947 
       
   948 void BookmarksToolBar::build()
       
   949 {
       
   950     clear();
       
   951     for (int i = 0; i < m_bookmarksModel->rowCount(m_root); ++i) {
       
   952         QModelIndex idx = m_bookmarksModel->index(i, 0, m_root);
       
   953         if (m_bookmarksModel->hasChildren(idx)) {
       
   954             QToolButton *button = new QToolButton(this);
       
   955             button->setPopupMode(QToolButton::InstantPopup);
       
   956             button->setArrowType(Qt::DownArrow);
       
   957             button->setText(idx.data().toString());
       
   958             ModelMenu *menu = new ModelMenu(this);
       
   959             connect(menu, SIGNAL(activated(const QModelIndex &)),
       
   960                     this, SLOT(activated(const QModelIndex &)));
       
   961             menu->setModel(m_bookmarksModel);
       
   962             menu->setRootIndex(idx);
       
   963             menu->addAction(new QAction(menu));
       
   964             button->setMenu(menu);
       
   965             button->setToolButtonStyle(Qt::ToolButtonTextOnly);
       
   966             QAction *a = addWidget(button);
       
   967             a->setText(idx.data().toString());
       
   968         } else {
       
   969             QAction *action = addAction(idx.data().toString());
       
   970             action->setData(idx.data(BookmarksModel::UrlRole));
       
   971         }
       
   972     }
       
   973 }
       
   974 
       
   975 void BookmarksToolBar::triggered(QAction *action)
       
   976 {
       
   977     QVariant v = action->data();
       
   978     if (v.canConvert<QUrl>()) {
       
   979         emit openUrl(v.toUrl());
       
   980     }
       
   981 }
       
   982 
       
   983 void BookmarksToolBar::activated(const QModelIndex &index)
       
   984 {
       
   985     emit openUrl(index.data(BookmarksModel::UrlRole).toUrl());
       
   986 }
       
   987