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