1 /* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2009 Pino Toscano <pino@kde.org>
3 SPDX-FileCopyrightText: 2009 Daivd Faure <faure@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "konqhistoryview.h"
9 #include "konqhistory.h"
10 #include "konq_historyprovider.h"
11 #include "konqhistorymodel.h"
12 #include "konqhistoryproxymodel.h"
13 #include "konqhistorysettings.h"
14
15 #include <KDialogJobUiDelegate>
16 #include <QAction>
17 #include <QApplication>
18 #include <QClipboard>
19 #include <QIcon>
20 #include <QLineEdit>
21 #include <QMenu>
22 #include <QMimeData>
23 #include <QTimer>
24 #include <QTreeView>
25 #include <QVBoxLayout>
26
27 #include <kactioncollection.h>
28 #include "konqdebug.h"
29 #include <KLocalizedString>
30 #include <kmessagebox.h>
31
32 #include <KIO/CommandLauncherJob>
33
KonqHistoryView(QWidget * parent)34 KonqHistoryView::KonqHistoryView(QWidget *parent)
35 : QWidget(parent)
36 , m_searchTimer(nullptr)
37 {
38 m_treeView = new QTreeView(this);
39 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
40 m_treeView->setHeaderHidden(true);
41
42 m_historyProxyModel = new KonqHistoryProxyModel(KonqHistorySettings::self(), m_treeView);
43 connect(m_treeView, &QTreeView::customContextMenuRequested, this, &KonqHistoryView::slotContextMenu);
44 m_historyModel = new KonqHistoryModel(m_historyProxyModel);
45 m_treeView->setModel(m_historyProxyModel);
46 m_historyProxyModel->setSourceModel(m_historyModel);
47 m_historyProxyModel->sort(0);
48
49 m_collection = new KActionCollection(this);
50 m_collection->addAssociatedWidget(m_treeView); // make shortcuts work
51 QAction *action = m_collection->addAction(QStringLiteral("open_new"));
52 action->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
53 action->setText(i18n("Open in New &Window"));
54 connect(action, &QAction::triggered, this, &KonqHistoryView::slotNewWindow);
55
56 action = m_collection->addAction(QStringLiteral("open_tab"));
57 action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
58 action->setText(i18n("Open in New Tab"));
59 connect(action, &QAction::triggered, this, &KonqHistoryView::slotNewTab);
60
61 action = m_collection->addAction(QStringLiteral("open_current_tab"));
62 action->setIcon(QIcon::fromTheme(QStringLiteral("window")));
63 action->setText(i18n("Open in Current Tab"));
64 connect(action, &QAction::triggered, this, &KonqHistoryView::slotCurrentTab);
65
66 action = m_collection->addAction(QStringLiteral("copylinklocation"));
67 action->setText(i18n("&Copy Link Address"));
68 connect(action, &QAction::triggered, this, &KonqHistoryView::slotCopyLinkLocation);
69
70 action = m_collection->addAction(QStringLiteral("remove"));
71 action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
72 action->setText(i18n("&Remove Entry"));
73 m_collection->setDefaultShortcut(action, QKeySequence(Qt::Key_Delete)); // #135966
74 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
75 connect(action, &QAction::triggered, this, &KonqHistoryView::slotRemoveEntry);
76
77 action = m_collection->addAction(QStringLiteral("clear"));
78 action->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history")));
79 action->setText(i18n("C&lear History"));
80 connect(action, &QAction::triggered, this, &KonqHistoryView::slotClearHistory);
81
82 action = m_collection->addAction(QStringLiteral("preferences"));
83 action->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
84 action->setText(i18n("&Preferences..."));
85 connect(action, &QAction::triggered, this, &KonqHistoryView::slotPreferences);
86
87 QActionGroup *sortGroup = new QActionGroup(this);
88 sortGroup->setExclusive(true);
89
90 action = m_collection->addAction(QStringLiteral("byName"));
91 action->setText(i18n("By &Name"));
92 action->setCheckable(true);
93 action->setData(QVariant::fromValue(0));
94 sortGroup->addAction(action);
95
96 action = m_collection->addAction(QStringLiteral("byDate"));
97 action->setText(i18n("By &Date"));
98 action->setCheckable(true);
99 action->setData(QVariant::fromValue(1));
100 sortGroup->addAction(action);
101
102 KonqHistorySettings *settings = KonqHistorySettings::self();
103 sortGroup->actions().at(settings->m_sortsByName ? 0 : 1)->setChecked(true);
104 connect(sortGroup, &QActionGroup::triggered, this, &KonqHistoryView::slotSortChange);
105
106 m_searchLineEdit = new QLineEdit(this);
107 m_searchLineEdit->setPlaceholderText(i18n("Search in history"));
108 m_searchLineEdit->setClearButtonEnabled(true);
109
110 connect(m_searchLineEdit, &QLineEdit::textChanged, this, &KonqHistoryView::slotFilterTextChanged);
111
112 QVBoxLayout *mainLayout = new QVBoxLayout(this);
113 mainLayout->setContentsMargins(0, 0, 0, 0);
114 mainLayout->addWidget(m_searchLineEdit);
115 mainLayout->addWidget(m_treeView);
116 }
117
slotContextMenu(const QPoint & pos)118 void KonqHistoryView::slotContextMenu(const QPoint &pos)
119 {
120 const QModelIndex index = m_treeView->indexAt(pos);
121 if (!index.isValid()) {
122 return;
123 }
124
125 const int nodeType = index.data(KonqHistory::TypeRole).toInt();
126
127 QMenu *menu = new QMenu(this);
128
129 if (nodeType == KonqHistory::HistoryType) {
130 menu->addAction(m_collection->action(QStringLiteral("open_new")));
131 menu->addAction(m_collection->action(QStringLiteral("open_tab")));
132 menu->addAction(m_collection->action(QStringLiteral("open_current_tab")));
133 menu->addAction(m_collection->action(QStringLiteral("copylinklocation")));
134 menu->addSeparator();
135 }
136
137 menu->addAction(m_collection->action(QStringLiteral("remove")));
138 menu->addAction(m_collection->action(QStringLiteral("clear")));
139 menu->addSeparator();
140 QMenu *sortMenu = menu->addMenu(i18nc("@action:inmenu Parent of 'By Name' and 'By Date'", "Sort"));
141 sortMenu->addAction(m_collection->action(QStringLiteral("byName")));
142 sortMenu->addAction(m_collection->action(QStringLiteral("byDate")));
143 menu->addSeparator();
144 menu->addAction(m_collection->action(QStringLiteral("preferences")));
145
146 menu->exec(m_treeView->viewport()->mapToGlobal(pos));
147
148 delete menu;
149 }
150
slotRemoveEntry()151 void KonqHistoryView::slotRemoveEntry()
152 {
153 const QModelIndex index = m_treeView->currentIndex();
154 if (!index.isValid()) {
155 return;
156 }
157
158 // TODO undo/redo support
159 m_historyModel->deleteItem(m_historyProxyModel->mapToSource(index));
160 }
161
slotClearHistory()162 void KonqHistoryView::slotClearHistory()
163 {
164 KGuiItem guiitem = KStandardGuiItem::clear();
165 guiitem.setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history")));
166
167 if (KMessageBox::warningContinueCancel(this,
168 i18n("Do you really want to clear the entire history?"),
169 i18nc("@title:window", "Clear History?"), guiitem)
170 == KMessageBox::Continue) {
171 KonqHistoryProvider::self()->emitClear();
172 }
173 }
174
slotPreferences()175 void KonqHistoryView::slotPreferences()
176 {
177 // Run the history sidebar settings.
178 KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell5 kcmhistory"));
179 job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
180 job->start();
181 }
182
slotSortChange(QAction * action)183 void KonqHistoryView::slotSortChange(QAction *action)
184 {
185 if (!action) {
186 return;
187 }
188
189 const int which = action->data().toInt();
190 KonqHistorySettings *settings = KonqHistorySettings::self();
191 settings->m_sortsByName = (which == 0);
192 settings->applySettings();
193 }
194
slotFilterTextChanged(const QString & text)195 void KonqHistoryView::slotFilterTextChanged(const QString &text)
196 {
197 Q_UNUSED(text);
198 if (!m_searchTimer) {
199 m_searchTimer = new QTimer(this);
200 m_searchTimer->setSingleShot(true);
201 connect(m_searchTimer, &QTimer::timeout, this, &KonqHistoryView::slotTimerTimeout);
202 }
203 m_searchTimer->start(600);
204 }
205
slotTimerTimeout()206 void KonqHistoryView::slotTimerTimeout()
207 {
208 m_historyProxyModel->setFilterFixedString(m_searchLineEdit->text());
209 }
210
treeView() const211 QTreeView *KonqHistoryView::treeView() const
212 {
213 return m_treeView;
214 }
215
lineEdit() const216 QLineEdit *KonqHistoryView::lineEdit() const
217 {
218 return m_searchLineEdit;
219 }
220
slotNewWindow()221 void KonqHistoryView::slotNewWindow()
222 {
223 const QUrl url = urlForIndex(m_treeView->currentIndex());
224 if (url.isValid()) {
225 emit openUrlInNewWindow(url);
226 }
227 }
228
slotNewTab()229 void KonqHistoryView::slotNewTab()
230 {
231 const QUrl url = urlForIndex(m_treeView->currentIndex());
232 if (url.isValid()) {
233 emit openUrlInNewTab(url);
234 }
235 }
236
slotCurrentTab()237 void KonqHistoryView::slotCurrentTab()
238 {
239 const QUrl url = urlForIndex(m_treeView->currentIndex());
240 if (url.isValid()) {
241 emit openUrlInCurrentTab(url);
242 }
243 }
244
urlForIndex(const QModelIndex & index) const245 QUrl KonqHistoryView::urlForIndex(const QModelIndex &index) const
246 {
247 if (!index.isValid() || (index.data(KonqHistory::TypeRole).toInt() != KonqHistory::HistoryType)) {
248 return QUrl();
249 }
250
251 return index.data(KonqHistory::UrlRole).toUrl();
252 }
253
254 // Code taken from KHTMLPopupGUIClient::slotCopyLinkLocation
slotCopyLinkLocation()255 void KonqHistoryView::slotCopyLinkLocation()
256 {
257 QUrl safeURL = urlForIndex(m_treeView->currentIndex()).adjusted(QUrl::RemovePassword);
258
259 // Set it in both the mouse selection and in the clipboard
260 QMimeData *mimeData = new QMimeData;
261 mimeData->setUrls(QList<QUrl>() << safeURL);
262 QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
263 QApplication::clipboard()->setMimeData(mimeData, QClipboard::Selection);
264 }
265