1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org>
4 SPDX-FileCopyrightText: 2006 Daniel Teske <teske@squorn.de>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kbookmarkmenu.h"
10#include "kbookmarkmenu_p.h"
11
12#include "../kbookmarksettings_p.h"
13#include "kbookmarkaction.h"
14#include "kbookmarkactionmenu.h"
15#include "kbookmarkcontextmenu.h"
16#include "kbookmarkdialog.h"
17#include "kbookmarkmanager.h"
18#include "kbookmarkowner.h"
19#include "kbookmarkswidgets_debug.h"
20#include "keditbookmarks_p.h"
21
22#include <KAuthorized>
23#include <KStandardAction>
24
25#include <QApplication>
26#include <QMenu>
27#include <QMessageBox>
28#include <QStandardPaths>
29
30/********************************************************************/
31/********************************************************************/
32/********************************************************************/
33class KBookmarkMenuPrivate
34{
35public:
36 QAction *newBookmarkFolderAction = nullptr;
37 QAction *addBookmarkAction = nullptr;
38 QAction *bookmarksToFolderAction = nullptr;
39 QAction *editBookmarksAction = nullptr;
40 bool browserMode = false;
41 bool isRoot;
42 bool dirty;
43 KBookmarkManager *manager;
44 KBookmarkOwner *owner;
45 QMenu *parentMenu;
46 QString parentAddress;
47};
48
49KBookmarkMenu::KBookmarkMenu(KBookmarkManager *manager, KBookmarkOwner *_owner, QMenu *_parentMenu)
50 : QObject()
51 , d(new KBookmarkMenuPrivate())
52{
53 d->isRoot = true;
54 d->manager = manager;
55 d->owner = _owner;
56 d->parentMenu = _parentMenu;
57 d->parentAddress = QString(); // TODO KBookmarkAdress::root
58 // TODO KDE5 find a QMenu equvalnet for this one
59 // m_parentMenu->setKeyboardShortcutsEnabled( true );
60
61 init();
62}
63
64void KBookmarkMenu::init()
65{
66 connect(sender: d->parentMenu, signal: &QMenu::aboutToShow, context: this, slot: &KBookmarkMenu::slotAboutToShow);
67
68 if (KBookmarkSettings::self()->m_contextmenu) {
69 d->parentMenu->setContextMenuPolicy(Qt::CustomContextMenu);
70 connect(sender: d->parentMenu, signal: &QWidget::customContextMenuRequested, context: this, slot: &KBookmarkMenu::slotCustomContextMenu);
71 }
72
73 connect(sender: d->manager, signal: &KBookmarkManager::changed, context: this, slot: &KBookmarkMenu::slotBookmarksChanged);
74
75 d->dirty = true;
76 addActions();
77}
78
79void KBookmarkMenu::addActions()
80{
81 if (d->isRoot) {
82 addAddBookmark();
83 addAddBookmarksList();
84 addNewFolder();
85 addEditBookmarks();
86 } else {
87 if (!d->parentMenu->actions().isEmpty()) {
88 d->parentMenu->addSeparator();
89 }
90
91 addOpenInTabs();
92 addAddBookmark();
93 addAddBookmarksList();
94 addNewFolder();
95 }
96}
97
98KBookmarkMenu::KBookmarkMenu(KBookmarkManager *mgr, KBookmarkOwner *_owner, QMenu *_parentMenu, const QString &parentAddress)
99 : QObject()
100 , d(new KBookmarkMenuPrivate())
101{
102 d->isRoot = false;
103 d->manager = mgr;
104 d->owner = _owner;
105 d->parentMenu = _parentMenu;
106 d->parentAddress = parentAddress;
107
108 connect(sender: _parentMenu, signal: &QMenu::aboutToShow, context: this, slot: &KBookmarkMenu::slotAboutToShow);
109 if (KBookmarkSettings::self()->m_contextmenu) {
110 d->parentMenu->setContextMenuPolicy(Qt::CustomContextMenu);
111 connect(sender: d->parentMenu, signal: &QWidget::customContextMenuRequested, context: this, slot: &KBookmarkMenu::slotCustomContextMenu);
112 }
113 d->dirty = true;
114}
115
116KBookmarkMenu::~KBookmarkMenu()
117{
118 qDeleteAll(c: m_lstSubMenus);
119 qDeleteAll(c: m_actions);
120}
121
122void KBookmarkMenu::ensureUpToDate()
123{
124 slotAboutToShow();
125}
126
127void KBookmarkMenu::slotAboutToShow()
128{
129 // Did the bookmarks change since the last time we showed them ?
130 if (d->dirty) {
131 d->dirty = false;
132 clear();
133 refill();
134 d->parentMenu->adjustSize();
135 }
136}
137
138void KBookmarkMenu::slotCustomContextMenu(const QPoint &pos)
139{
140 QAction *action = d->parentMenu->actionAt(pos);
141 QMenu *menu = contextMenu(action);
142 if (!menu) {
143 return;
144 }
145 menu->setAttribute(Qt::WA_DeleteOnClose);
146 menu->popup(pos: d->parentMenu->mapToGlobal(pos));
147}
148
149QMenu *KBookmarkMenu::contextMenu(QAction *action)
150{
151 KBookmarkActionInterface *act = dynamic_cast<KBookmarkActionInterface *>(action);
152 if (!act) {
153 return nullptr;
154 }
155 return new KBookmarkContextMenu(act->bookmark(), d->manager, d->owner);
156}
157
158bool KBookmarkMenu::isRoot() const
159{
160 return d->isRoot;
161}
162
163bool KBookmarkMenu::isDirty() const
164{
165 return d->dirty;
166}
167
168QString KBookmarkMenu::parentAddress() const
169{
170 return d->parentAddress;
171}
172
173KBookmarkManager *KBookmarkMenu::manager() const
174{
175 return d->manager;
176}
177
178KBookmarkOwner *KBookmarkMenu::owner() const
179{
180 return d->owner;
181}
182
183QMenu *KBookmarkMenu::parentMenu() const
184{
185 return d->parentMenu;
186}
187
188/********************************************************************/
189/********************************************************************/
190/********************************************************************/
191
192/********************************************************************/
193/********************************************************************/
194/********************************************************************/
195
196void KBookmarkMenu::slotBookmarksChanged(const QString &groupAddress)
197{
198 qCDebug(KBOOKMARKSWIDGETS_LOG) << "KBookmarkMenu::slotBookmarksChanged groupAddress: " << groupAddress;
199 if (groupAddress == d->parentAddress) {
200 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkMenu::slotBookmarksChanged -> setting m_bDirty on " << groupAddress;
201 d->dirty = true;
202 } else {
203 // Iterate recursively into child menus
204 for (QList<KBookmarkMenu *>::iterator it = m_lstSubMenus.begin(), end = m_lstSubMenus.end(); it != end; ++it) {
205 (*it)->slotBookmarksChanged(groupAddress);
206 }
207 }
208}
209
210void KBookmarkMenu::clear()
211{
212 qDeleteAll(c: m_lstSubMenus);
213 m_lstSubMenus.clear();
214
215 for (QList<QAction *>::iterator it = m_actions.begin(), end = m_actions.end(); it != end; ++it) {
216 d->parentMenu->removeAction(action: *it);
217 delete *it;
218 }
219
220 d->parentMenu->clear();
221 m_actions.clear();
222}
223
224void KBookmarkMenu::refill()
225{
226 // qCDebug(KBOOKMARKS_LOG) << "KBookmarkMenu::refill()";
227 if (d->isRoot) {
228 addActions();
229 }
230 fillBookmarks();
231 if (!d->isRoot) {
232 addActions();
233 }
234}
235
236void KBookmarkMenu::addOpenInTabs()
237{
238 if (!d->owner || !d->owner->supportsTabs() || !KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
239 return;
240 }
241
242 const QString title = tr(s: "Open Folder in Tabs", c: "@action:inmenu");
243
244 QAction *paOpenFolderInTabs = new QAction(title, this);
245 paOpenFolderInTabs->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
246 paOpenFolderInTabs->setToolTip(tr(s: "Open all bookmarks in this folder as a new tab", c: "@info:tooltip"));
247 paOpenFolderInTabs->setStatusTip(paOpenFolderInTabs->toolTip());
248 connect(sender: paOpenFolderInTabs, signal: &QAction::triggered, context: this, slot: &KBookmarkMenu::slotOpenFolderInTabs);
249
250 d->parentMenu->addAction(action: paOpenFolderInTabs);
251 m_actions.append(t: paOpenFolderInTabs);
252}
253
254void KBookmarkMenu::addAddBookmarksList()
255{
256 if (!d->owner || !d->owner->enableOption(option: KBookmarkOwner::ShowAddBookmark) || !d->owner->supportsTabs()
257 || !KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
258 return;
259 }
260
261 if (!d->bookmarksToFolderAction) {
262 const QString title = tr(s: "Bookmark Tabs as Folder...", c: "@action:inmenu");
263 d->bookmarksToFolderAction = new QAction(title, this);
264
265 if (d->isRoot) {
266 d->bookmarksToFolderAction->setObjectName(QStringLiteral("add_bookmarks_list"));
267 }
268
269 d->bookmarksToFolderAction->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new-list")));
270 d->bookmarksToFolderAction->setToolTip(tr(s: "Add a folder of bookmarks for all open tabs", c: "@info:tooltip"));
271 d->bookmarksToFolderAction->setStatusTip(d->bookmarksToFolderAction->toolTip());
272 connect(sender: d->bookmarksToFolderAction, signal: &QAction::triggered, context: this, slot: &KBookmarkMenu::slotAddBookmarksList);
273 }
274
275 d->parentMenu->addAction(action: d->bookmarksToFolderAction);
276}
277
278void KBookmarkMenu::addAddBookmark()
279{
280 if (!d->owner || !d->owner->enableOption(option: KBookmarkOwner::ShowAddBookmark) || !KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
281 return;
282 }
283
284 if (!d->addBookmarkAction) {
285 d->addBookmarkAction = KStandardAction::addBookmark(recvr: this, slot: &KBookmarkMenu::slotAddBookmark, parent: this);
286 if (d->isRoot) {
287 d->addBookmarkAction->setObjectName(QStringLiteral("add_bookmark"));
288 }
289
290 if (!d->isRoot) {
291 d->addBookmarkAction->setShortcut(QKeySequence());
292 }
293 }
294
295 d->parentMenu->addAction(action: d->addBookmarkAction);
296}
297
298void KBookmarkMenu::addEditBookmarks()
299{
300 if ((d->owner && !d->owner->enableOption(option: KBookmarkOwner::ShowEditBookmark))
301 || QStandardPaths::findExecutable(QStringLiteral(KEDITBOOKMARKS_BINARY)).isEmpty() || !KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
302 return;
303 }
304
305 d->editBookmarksAction = KStandardAction::editBookmarks(recvr: this, slot: &KBookmarkMenu::slotEditBookmarks, parent: this);
306 d->editBookmarksAction->setObjectName(QStringLiteral("edit_bookmarks"));
307
308 d->parentMenu->addAction(action: d->editBookmarksAction);
309 d->editBookmarksAction->setToolTip(tr(s: "Edit your bookmark collection in a separate window", c: "@info:tooltip"));
310 d->editBookmarksAction->setStatusTip(d->editBookmarksAction->toolTip());
311}
312
313void KBookmarkMenu::slotEditBookmarks()
314{
315 KEditBookmarks editBookmarks;
316 editBookmarks.setBrowserMode(d->browserMode);
317 auto result = editBookmarks.openForFile(file: d->manager->path());
318
319 if (!result.sucess()) {
320 QMessageBox::critical(parent: QApplication::activeWindow(), title: QApplication::applicationDisplayName(), text: result.errorMessage());
321 }
322}
323
324void KBookmarkMenu::addNewFolder()
325{
326 if (!d->owner || !d->owner->enableOption(option: KBookmarkOwner::ShowAddBookmark) || !KAuthorized::authorizeAction(QStringLiteral("bookmarks"))) {
327 return;
328 }
329
330 if (!d->newBookmarkFolderAction) {
331 d->newBookmarkFolderAction = new QAction(tr(s: "New Bookmark Folder...", c: "@action:inmenu"), this);
332 d->newBookmarkFolderAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
333 d->newBookmarkFolderAction->setToolTip(tr(s: "Create a new bookmark folder in this menu", c: "@info:tooltip"));
334 d->newBookmarkFolderAction->setStatusTip(d->newBookmarkFolderAction->toolTip());
335
336 if (d->isRoot) {
337 d->newBookmarkFolderAction->setObjectName(QStringLiteral("new_bookmark_folder"));
338 }
339
340 connect(sender: d->newBookmarkFolderAction, signal: &QAction::triggered, context: this, slot: &KBookmarkMenu::slotNewFolder);
341 }
342
343 d->parentMenu->addAction(action: d->newBookmarkFolderAction);
344}
345
346void KBookmarkMenu::fillBookmarks()
347{
348 KBookmarkGroup parentBookmark = d->manager->findByAddress(address: d->parentAddress).toGroup();
349 Q_ASSERT(!parentBookmark.isNull());
350
351 if (d->isRoot && !parentBookmark.first().isNull()) { // at least one bookmark
352 d->parentMenu->addSeparator();
353 }
354
355 for (KBookmark bm = parentBookmark.first(); !bm.isNull(); bm = parentBookmark.next(current: bm)) {
356 d->parentMenu->addAction(action: actionForBookmark(bm));
357 }
358}
359
360QAction *KBookmarkMenu::actionForBookmark(const KBookmark &bm)
361{
362 if (bm.isGroup()) {
363 // qCDebug(KBOOKMARKS_LOG) << "Creating bookmark submenu named " << bm.text();
364 KActionMenu *actionMenu = new KBookmarkActionMenu(bm, this);
365 m_actions.append(t: actionMenu);
366 KBookmarkMenu *subMenu = new KBookmarkMenu(d->manager, d->owner, actionMenu->menu(), bm.address());
367 m_lstSubMenus.append(t: subMenu);
368 return actionMenu;
369 } else if (bm.isSeparator()) {
370 QAction *sa = new QAction(this);
371 sa->setSeparator(true);
372 m_actions.append(t: sa);
373 return sa;
374 } else {
375 // qCDebug(KBOOKMARKS_LOG) << "Creating bookmark menu item for " << bm.text();
376 QAction *action = new KBookmarkAction(bm, d->owner, this);
377 m_actions.append(t: action);
378 return action;
379 }
380}
381
382void KBookmarkMenu::slotAddBookmarksList()
383{
384 if (!d->owner || !d->owner->supportsTabs()) {
385 return;
386 }
387
388 KBookmarkGroup parentBookmark = d->manager->findByAddress(address: d->parentAddress).toGroup();
389
390 KBookmarkDialog *dlg = new KBookmarkDialog(d->manager, QApplication::activeWindow());
391 dlg->addBookmarks(list: d->owner->currentBookmarkList(), name: QLatin1String(""), parent: parentBookmark);
392 delete dlg;
393}
394
395void KBookmarkMenu::slotAddBookmark()
396{
397 if (!d->owner) {
398 return;
399 }
400 if (d->owner->currentTitle().isEmpty() && d->owner->currentUrl().isEmpty()) {
401 return;
402 }
403 KBookmarkGroup parentBookmark = d->manager->findByAddress(address: d->parentAddress).toGroup();
404
405 if (KBookmarkSettings::self()->m_advancedaddbookmark) {
406 KBookmarkDialog *dlg = new KBookmarkDialog(d->manager, QApplication::activeWindow());
407 dlg->addBookmark(title: d->owner->currentTitle(), url: d->owner->currentUrl(), icon: d->owner->currentIcon(), parent: parentBookmark);
408 delete dlg;
409 } else {
410 parentBookmark.addBookmark(text: d->owner->currentTitle(), url: d->owner->currentUrl(), icon: d->owner->currentIcon());
411 d->manager->emitChanged(group: parentBookmark);
412 }
413}
414
415void KBookmarkMenu::slotOpenFolderInTabs()
416{
417 d->owner->openFolderinTabs(bm: d->manager->findByAddress(address: d->parentAddress).toGroup());
418}
419
420void KBookmarkMenu::slotNewFolder()
421{
422 if (!d->owner) {
423 return; // this view doesn't handle bookmarks...
424 }
425 KBookmarkGroup parentBookmark = d->manager->findByAddress(address: d->parentAddress).toGroup();
426 Q_ASSERT(!parentBookmark.isNull());
427 KBookmarkDialog *dlg = new KBookmarkDialog(d->manager, QApplication::activeWindow());
428 dlg->createNewFolder(name: QLatin1String(""), parent: parentBookmark);
429 delete dlg;
430}
431
432QAction *KBookmarkMenu::addBookmarkAction() const
433{
434 return d->addBookmarkAction;
435}
436
437QAction *KBookmarkMenu::bookmarkTabsAsFolderAction() const
438{
439 return d->bookmarksToFolderAction;
440}
441
442QAction *KBookmarkMenu::newBookmarkFolderAction() const
443{
444 return d->newBookmarkFolderAction;
445}
446
447QAction *KBookmarkMenu::editBookmarksAction() const
448{
449 return d->editBookmarksAction;
450}
451
452void KBookmarkMenu::setBrowserMode(bool browserMode)
453{
454 d->browserMode = browserMode;
455}
456
457bool KBookmarkMenu::browserMode() const
458{
459 return d->browserMode;
460}
461
462#include "moc_kbookmarkmenu.cpp"
463

source code of kbookmarks/src/widgets/kbookmarkmenu.cpp