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 }