1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Assistant of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include "bookmarkmanagerwidget.h"
29#include "bookmarkitem.h"
30#include "bookmarkmodel.h"
31#include "tracer.h"
32#include "xbelsupport.h"
33
34#include <QtCore/QCoreApplication>
35#include <QtCore/QFile>
36#include <QtCore/QUrl>
37
38#include <QtGui/QCloseEvent>
39#include <QtWidgets/QFileDialog>
40#include <QtGui/QKeySequence>
41#include <QtWidgets/QMessageBox>
42#include <QtWidgets/QShortcut>
43
44QT_BEGIN_NAMESPACE
45
46BookmarkManagerWidget::BookmarkManagerWidget(BookmarkModel *sourceModel,
47 QWidget *parent)
48 : QWidget(parent)
49 , bookmarkModel(sourceModel)
50{
51 TRACE_OBJ
52 ui.setupUi(this);
53
54 ui.treeView->setModel(bookmarkModel);
55
56 ui.treeView->expandAll();
57 ui.treeView->installEventFilter(filterObj: this);
58 ui.treeView->viewport()->installEventFilter(filterObj: this);
59 ui.treeView->setContextMenuPolicy(Qt::CustomContextMenu);
60
61 connect(sender: ui.treeView, signal: &QWidget::customContextMenuRequested,
62 receiver: this, slot: &BookmarkManagerWidget::customContextMenuRequested);
63
64 connect(sender: ui.remove, signal: &QAbstractButton::clicked,
65 context: this, slot: [this]() { removeItem(); });
66 connect(sender: ui.lineEdit, signal: &QLineEdit::textChanged,
67 receiver: this, slot: &BookmarkManagerWidget::textChanged);
68 QShortcut *shortcut = new QShortcut(QKeySequence::Find, ui.lineEdit);
69 connect(sender: shortcut, signal: &QShortcut::activated,
70 receiver: ui.lineEdit, slot: QOverload<>::of(ptr: &QWidget::setFocus));
71
72 importExportMenu.addAction(text: tr(s: "Import..."), object: this,
73 slot: &BookmarkManagerWidget::importBookmarks);
74 importExportMenu.addAction(text: tr(s: "Export..."), object: this,
75 slot: &BookmarkManagerWidget::exportBookmarks);
76 ui.importExport->setMenu(&importExportMenu);
77
78 shortcut = new QShortcut(QKeySequence::FindNext, this);
79 connect(sender: shortcut, signal: &QShortcut::activated,
80 receiver: this, slot: &BookmarkManagerWidget::findNext);
81 shortcut = new QShortcut(QKeySequence::FindPrevious, this);
82 connect(sender: shortcut, signal: &QShortcut::activated,
83 receiver: this, slot: &BookmarkManagerWidget::findPrevious);
84
85 connect(sender: bookmarkModel, signal: &QAbstractItemModel::rowsRemoved,
86 receiver: this, slot: &BookmarkManagerWidget::refeshBookmarkCache);
87 connect(sender: bookmarkModel, signal: &QAbstractItemModel::rowsInserted,
88 receiver: this, slot: &BookmarkManagerWidget::refeshBookmarkCache);
89 connect(sender: bookmarkModel, signal: &QAbstractItemModel::dataChanged,
90 receiver: this, slot: &BookmarkManagerWidget::refeshBookmarkCache);
91
92 ui.treeView->setCurrentIndex(ui.treeView->indexAt(p: QPoint(2, 2)));
93}
94
95BookmarkManagerWidget::~BookmarkManagerWidget()
96{
97 TRACE_OBJ
98}
99
100void BookmarkManagerWidget::closeEvent(QCloseEvent *event)
101{
102 TRACE_OBJ
103 event->accept();
104 emit managerWidgetAboutToClose();
105}
106
107void BookmarkManagerWidget::renameItem(const QModelIndex &index)
108{
109 TRACE_OBJ
110 // check if we should rename the "Bookmarks Menu", bail
111 if (!bookmarkModel->parent(index).isValid())
112 return;
113
114 bookmarkModel->setItemsEditable(true);
115 ui.treeView->edit(index);
116 bookmarkModel->setItemsEditable(false);
117}
118
119static int nextIndex(int current, int count, bool forward)
120{
121 TRACE_OBJ
122 if (current >= 0)
123 return (forward ? (current + 1) : ((current - 1) + count)) % count;
124 return 0;
125}
126
127void BookmarkManagerWidget::selectNextIndex(bool direction) const
128{
129 QModelIndex current = ui.treeView->currentIndex();
130 if (current.isValid() && !cache.isEmpty()) {
131 current = cache.at(i: nextIndex(current: cache.indexOf(t: current), count: cache.count(),
132 forward: direction));
133 }
134 ui.treeView->setCurrentIndex(current);
135}
136
137bool BookmarkManagerWidget::eventFilter(QObject *object, QEvent *event)
138{
139 TRACE_OBJ
140 if (object != ui.treeView && object != ui.treeView->viewport())
141 return QWidget::eventFilter(watched: object, event);
142
143 if (event->type() == QEvent::KeyPress) {
144 QKeyEvent *ke = static_cast<QKeyEvent*>(event);
145 switch (ke->key()) {
146 case Qt::Key_F2:
147 renameItem(index: ui.treeView->currentIndex());
148 break;
149
150 case Qt::Key_Delete:
151 removeItem(index: ui.treeView->currentIndex());
152 break;
153
154 default: break;
155 }
156 }
157
158 if (event->type() == QEvent::MouseButtonRelease) {
159 QMouseEvent *me = static_cast<QMouseEvent*>(event);
160 switch (me->button()) {
161 case Qt::LeftButton:
162 if (me->modifiers() & Qt::ControlModifier)
163 setSourceFromIndex(index: ui.treeView->currentIndex(), newTab: true);
164 break;
165
166 case Qt::MiddleButton:
167 setSourceFromIndex(index: ui.treeView->currentIndex(), newTab: true);
168 break;
169
170 default: break;
171 }
172 }
173 return QObject::eventFilter(watched: object, event);
174}
175
176void BookmarkManagerWidget::findNext()
177{
178 TRACE_OBJ
179 selectNextIndex(direction: true);
180}
181
182void BookmarkManagerWidget::findPrevious()
183{
184 TRACE_OBJ
185 selectNextIndex(direction: false);
186}
187
188void BookmarkManagerWidget::importBookmarks()
189{
190 TRACE_OBJ
191 const QString &fileName = QFileDialog::getOpenFileName(parent: nullptr, caption: tr(s: "Open File"),
192 dir: QDir::currentPath(), filter: tr(s: "Files (*.xbel)"));
193
194 if (fileName.isEmpty())
195 return;
196
197 QFile file(fileName);
198 if (file.open(flags: QIODevice::ReadOnly)) {
199 XbelReader reader(bookmarkModel);
200 reader.readFromFile(device: &file);
201 }
202}
203
204void BookmarkManagerWidget::exportBookmarks()
205{
206 TRACE_OBJ
207 QString fileName = QFileDialog::getSaveFileName(parent: nullptr, caption: tr(s: "Save File"),
208 dir: QLatin1String("untitled.xbel"), filter: tr(s: "Files (*.xbel)"));
209
210 const QLatin1String suffix(".xbel");
211 if (!fileName.endsWith(s: suffix))
212 fileName.append(s: suffix);
213
214 QFile file(fileName);
215 if (file.open(flags: QIODevice::WriteOnly)) {
216 XbelWriter writer(bookmarkModel);
217 writer.writeToFile(device: &file);
218 } else {
219 QMessageBox::information(parent: this, title: tr(s: "Qt Assistant"),
220 text: tr(s: "Unable to save bookmarks."), button0Text: tr(s: "OK"));
221 }
222}
223
224void BookmarkManagerWidget::refeshBookmarkCache()
225{
226 TRACE_OBJ
227 cache.clear();
228
229 const QString &text = ui.lineEdit->text();
230 if (!text.isEmpty())
231 cache = bookmarkModel->indexListFor(label: text);
232}
233
234void BookmarkManagerWidget::textChanged(const QString &/*text*/)
235{
236 TRACE_OBJ
237 refeshBookmarkCache();
238 if (!cache.isEmpty())
239 ui.treeView->setCurrentIndex(cache.at(i: 0));
240}
241
242void BookmarkManagerWidget::removeItem(const QModelIndex &index)
243{
244 TRACE_OBJ
245 QModelIndex current = index.isValid() ? index : ui.treeView->currentIndex();
246 if (!current.parent().isValid() && current.row() < 2)
247 return; // check if we should delete the "Bookmarks Menu", bail
248
249 if (bookmarkModel->hasChildren(parent: current)) {
250 int value = QMessageBox::question(parent: this, title: tr(s: "Remove"), text: tr(s: "You are going"
251 "to delete a Folder, this will also<br> remove it's content. Are "
252 "you sure to continue?"),
253 buttons: QMessageBox::Yes | QMessageBox::Cancel, defaultButton: QMessageBox::Cancel);
254 if (value == QMessageBox::Cancel)
255 return;
256 }
257 bookmarkModel->removeItem(index: current);
258}
259
260void BookmarkManagerWidget::customContextMenuRequested(const QPoint &point)
261{
262 TRACE_OBJ
263 const QModelIndex &index = ui.treeView->indexAt(p: point);
264 if (!index.isValid())
265 return;
266
267 // check if we should open the menu on "Bookmarks Menu", bail
268 if (!bookmarkModel->parent(index).isValid())
269 return;
270
271 QAction *remove = nullptr;
272 QAction *rename = nullptr;
273 QAction *showItem = nullptr;
274 QAction *showItemInNewTab = nullptr;
275
276 QMenu menu;
277 if (bookmarkModel->data(index, role: UserRoleFolder).toBool()) {
278 remove = menu.addAction(text: tr(s: "Delete Folder"));
279 rename = menu.addAction(text: tr(s: "Rename Folder"));
280 } else {
281 showItem = menu.addAction(text: tr(s: "Show Bookmark"));
282 showItemInNewTab = menu.addAction(text: tr(s: "Show Bookmark in New Tab"));
283 menu.addSeparator();
284 remove = menu.addAction(text: tr(s: "Delete Bookmark"));
285 rename = menu.addAction(text: tr(s: "Rename Bookmark"));
286 }
287
288 QAction *pickedAction = menu.exec(pos: ui.treeView->mapToGlobal(point));
289 if (pickedAction == rename)
290 renameItem(index);
291 else if (pickedAction == remove)
292 removeItem(index);
293 else if (pickedAction == showItem || pickedAction == showItemInNewTab)
294 setSourceFromIndex(index, newTab: pickedAction == showItemInNewTab);
295}
296
297void
298BookmarkManagerWidget::setSourceFromIndex(const QModelIndex &index, bool newTab)
299{
300 TRACE_OBJ
301 if (bookmarkModel->data(index, role: UserRoleFolder).toBool())
302 return;
303
304 const QVariant &data = bookmarkModel->data(index, role: UserRoleUrl);
305 if (data.canConvert<QUrl>()) {
306 if (newTab)
307 emit setSourceInNewTab(data.toUrl());
308 else
309 emit setSource(data.toUrl());
310 }
311}
312
313QT_END_NAMESPACE
314

source code of qttools/src/assistant/assistant/bookmarkmanagerwidget.cpp