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

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