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 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | BookmarkManagerWidget::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 | |
95 | BookmarkManagerWidget::~BookmarkManagerWidget() |
96 | { |
97 | TRACE_OBJ |
98 | } |
99 | |
100 | void BookmarkManagerWidget::closeEvent(QCloseEvent *event) |
101 | { |
102 | TRACE_OBJ |
103 | event->accept(); |
104 | emit managerWidgetAboutToClose(); |
105 | } |
106 | |
107 | void 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 | |
119 | static 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 | |
127 | void 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 | |
137 | bool 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 | |
176 | void BookmarkManagerWidget::findNext() |
177 | { |
178 | TRACE_OBJ |
179 | selectNextIndex(direction: true); |
180 | } |
181 | |
182 | void BookmarkManagerWidget::findPrevious() |
183 | { |
184 | TRACE_OBJ |
185 | selectNextIndex(direction: false); |
186 | } |
187 | |
188 | void 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 | |
204 | void 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 | |
224 | void 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 | |
234 | void 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 | |
242 | void 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 | |
260 | void BookmarkManagerWidget::(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 ; |
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 | |
297 | void |
298 | BookmarkManagerWidget::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 | |
313 | QT_END_NAMESPACE |
314 | |