|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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(QModelIndex)), |
|
737 this, SLOT(activated(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(QModelIndex)), |
|
796 this, SLOT(open())); |
|
797 tree->setContextMenuPolicy(Qt::CustomContextMenu); |
|
798 connect(tree, SIGNAL(customContextMenuRequested(QPoint)), |
|
799 this, SLOT(customContextMenuRequested(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(QModelIndex,int,int)), this, SLOT(build())); |
|
888 connect(m_bookmarksModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(build())); |
|
889 connect(m_bookmarksModel, SIGNAL(dataChanged(QModelIndex,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(QModelIndex)), |
|
960 this, SLOT(activated(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 |