KConfigWidgets

krecentfilesaction.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <[email protected]>
4  SPDX-FileCopyrightText: 1999 Simon Hausmann <[email protected]>
5  SPDX-FileCopyrightText: 2000 Nicolas Hadacek <[email protected]>
6  SPDX-FileCopyrightText: 2000 Kurt Granroth <[email protected]>
7  SPDX-FileCopyrightText: 2000 Michael Koch <[email protected]>
8  SPDX-FileCopyrightText: 2001 Holger Freyther <[email protected]>
9  SPDX-FileCopyrightText: 2002 Ellis Whitehead <[email protected]>
10  SPDX-FileCopyrightText: 2002 Joseph Wenninger <[email protected]>
11  SPDX-FileCopyrightText: 2003 Andras Mantia <[email protected]>
12  SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <[email protected]>
13 
14  SPDX-License-Identifier: LGPL-2.0-only
15 */
16 
17 #include "krecentfilesaction.h"
18 #include "krecentfilesaction_p.h"
19 
20 #include <QActionGroup>
21 #include <QDir>
22 #include <QFile>
23 #include <QGuiApplication>
24 #include <QMenu>
25 #include <QScreen>
26 
27 #include <KConfig>
28 #include <KConfigGroup>
29 #include <KLocalizedString>
30 
32  : KSelectAction(parent)
33  , d_ptr(new KRecentFilesActionPrivate(this))
34 {
35  Q_D(KRecentFilesAction);
36  d->init();
37 }
38 
40  : KSelectAction(parent)
41  , d_ptr(new KRecentFilesActionPrivate(this))
42 {
43  Q_D(KRecentFilesAction);
44  d->init();
45 
46  // Want to keep the ampersands
47  setText(text);
48 }
49 
51  : KSelectAction(parent)
52  , d_ptr(new KRecentFilesActionPrivate(this))
53 {
54  Q_D(KRecentFilesAction);
55  d->init();
56 
57  setIcon(icon);
58  // Want to keep the ampersands
59  setText(text);
60 }
61 
62 void KRecentFilesActionPrivate::init()
63 {
64  Q_Q(KRecentFilesAction);
65  delete q->menu();
66  q->setMenu(new QMenu());
67  q->setToolBarMode(KSelectAction::MenuMode);
68  m_noEntriesAction = q->menu()->addAction(i18n("No Entries"));
69  m_noEntriesAction->setObjectName(QStringLiteral("no_entries"));
70  m_noEntriesAction->setEnabled(false);
71  clearSeparator = q->menu()->addSeparator();
72  clearSeparator->setVisible(false);
73  clearSeparator->setObjectName(QStringLiteral("separator"));
74  clearAction = q->menu()->addAction(i18n("Clear List"), q, &KRecentFilesAction::clear);
75  clearAction->setObjectName(QStringLiteral("clear_action"));
76  clearAction->setVisible(false);
77  q->setEnabled(false);
78  q->connect(q, qOverload<QAction *>(&KSelectAction::triggered), q, [this](QAction *action) {
79  urlSelected(action);
80  });
81 }
82 
84 
85 void KRecentFilesActionPrivate::urlSelected(QAction *action)
86 {
87  Q_Q(KRecentFilesAction);
88  Q_EMIT q->urlSelected(m_urls[action]);
89 }
90 
92 {
93  Q_D(const KRecentFilesAction);
94  return d->m_maxItems;
95 }
96 
98 {
99  Q_D(KRecentFilesAction);
100  // set new maxItems
101  d->m_maxItems = std::max(maxItems, 0);
102 
103  // remove all excess items
104  while (selectableActionGroup()->actions().count() > d->m_maxItems) {
105  delete removeAction(selectableActionGroup()->actions().last());
106  }
107 }
108 
109 static QString titleWithSensibleWidth(const QString &nameValue, const QString &value)
110 {
111  // Calculate 3/4 of screen geometry, we do not want
112  // action titles to be bigger than that
113  // Since we do not know in which screen we are going to show
114  // we choose the min of all the screens
115  int maxWidthForTitles = INT_MAX;
116  const auto screens = QGuiApplication::screens();
117  for (QScreen *screen : screens) {
118  maxWidthForTitles = qMin(maxWidthForTitles, screen->availableGeometry().width() * 3 / 4);
119  }
120  const QFontMetrics fontMetrics = QFontMetrics(QFont());
121 
122  QString title = nameValue + QLatin1String(" [") + value + QLatin1Char(']');
123  const int nameWidth = fontMetrics.boundingRect(title).width();
124  if (nameWidth > maxWidthForTitles) {
125  // If it does not fit, try to cut only the whole path, though if the
126  // name is too long (more than 3/4 of the whole text) we cut it a bit too
127  const int nameValueMaxWidth = maxWidthForTitles * 3 / 4;
128  QString cutNameValue;
129  QString cutValue;
130  if (nameWidth > nameValueMaxWidth) {
131  cutNameValue = fontMetrics.elidedText(nameValue, Qt::ElideMiddle, nameValueMaxWidth);
132  cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameValueMaxWidth);
133  } else {
134  cutNameValue = nameValue;
135  cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameWidth);
136  }
137  title = cutNameValue + QLatin1String(" [") + cutValue + QLatin1Char(']');
138  }
139  return title;
140 }
141 
142 void KRecentFilesAction::addUrl(const QUrl &_url, const QString &name)
143 {
144  Q_D(KRecentFilesAction);
145 
146  // ensure we never add items if we want none
147  if (d->m_maxItems == 0) {
148  return;
149  }
150 
151  /**
152  * Create a deep copy here, because if _url is the parameter from
153  * urlSelected() signal, we will delete it in the removeAction() call below.
154  * but access it again in the addAction call... => crash
155  */
156  const QUrl url(_url);
157 
158  if (url.isLocalFile() && url.toLocalFile().startsWith(QDir::tempPath())) {
159  return;
160  }
161  const QString tmpName = name.isEmpty() ? url.fileName() : name;
162  const QString pathOrUrl(url.toDisplayString(QUrl::PreferLocalFile));
163 
164 #ifdef Q_OS_WIN
165  const QString file = url.isLocalFile() ? QDir::toNativeSeparators(pathOrUrl) : pathOrUrl;
166 #else
167  const QString file = pathOrUrl;
168 #endif
169 
170  // remove file if already in list
171  const auto lstActions = selectableActionGroup()->actions();
172  for (QAction *action : lstActions) {
173  const QString urlStr = d->m_urls[action].toDisplayString(QUrl::PreferLocalFile);
174 #ifdef Q_OS_WIN
175  const QString tmpFileName = url.isLocalFile() ? QDir::toNativeSeparators(urlStr) : urlStr;
176  if (tmpFileName.endsWith(file, Qt::CaseInsensitive))
177 #else
178  if (urlStr.endsWith(file))
179 #endif
180  {
181  removeAction(action)->deleteLater();
182  break;
183  }
184  }
185  // remove oldest item if already maxitems in list
186  Q_ASSERT(d->m_maxItems > 0);
187  if (selectableActionGroup()->actions().count() == d->m_maxItems) {
188  // remove oldest added item
189  delete removeAction(selectableActionGroup()->actions().first());
190  }
191 
192  d->m_noEntriesAction->setVisible(false);
193  d->clearSeparator->setVisible(true);
194  d->clearAction->setVisible(true);
195  setEnabled(true);
196  // add file to list
197  const QString title = titleWithSensibleWidth(tmpName, file);
198  QAction *action = new QAction(title, selectableActionGroup());
199  addAction(action, url, tmpName);
200 }
201 
202 void KRecentFilesAction::addAction(QAction *action, const QUrl &url, const QString &name)
203 {
204  Q_D(KRecentFilesAction);
205 
206  menu()->insertAction(menu()->actions().value(0), action);
207  d->m_shortNames.insert(action, name);
208  d->m_urls.insert(action, url);
209 }
210 
212 {
213  Q_D(KRecentFilesAction);
215 
216  d->m_shortNames.remove(action);
217  d->m_urls.remove(action);
218 
219  return action;
220 }
221 
223 {
224  Q_D(KRecentFilesAction);
225  for (QMap<QAction *, QUrl>::ConstIterator it = d->m_urls.constBegin(); it != d->m_urls.constEnd(); ++it) {
226  if (it.value() == url) {
227  delete removeAction(it.key());
228  return;
229  }
230  }
231 }
232 
234 {
235  Q_D(const KRecentFilesAction);
236  return d->m_urls.values();
237 }
238 
240 {
241  clearEntries();
243 }
244 
245 void KRecentFilesAction::clearEntries()
246 {
247  Q_D(KRecentFilesAction);
249  d->m_shortNames.clear();
250  d->m_urls.clear();
251  d->m_noEntriesAction->setVisible(true);
252  d->clearSeparator->setVisible(false);
253  d->clearAction->setVisible(false);
254  setEnabled(false);
255 }
256 
258 {
259  Q_D(KRecentFilesAction);
260  clearEntries();
261 
262  QString key;
263  QString value;
264  QString nameKey;
265  QString nameValue;
266  QString title;
267  QUrl url;
268 
269  KConfigGroup cg = _config;
270  if (cg.name().isEmpty()) {
271  cg = KConfigGroup(cg.config(), "RecentFiles");
272  }
273 
274  bool thereAreEntries = false;
275  // read file list
276  for (int i = 1; i <= d->m_maxItems; i++) {
277  key = QStringLiteral("File%1").arg(i);
278  value = cg.readPathEntry(key, QString());
279  if (value.isEmpty()) {
280  continue;
281  }
282  url = QUrl::fromUserInput(value);
283 
284  // Don't restore if file doesn't exist anymore
285  if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) {
286  continue;
287  }
288 
289  // Don't restore where the url is already known (eg. broken config)
290  auto containsUrl = std::find(d->m_urls.cbegin(), d->m_urls.cend(), url) != d->m_urls.cend();
291  if (containsUrl) {
292  continue;
293  }
294 
295 #ifdef Q_OS_WIN
296  // convert to backslashes
297  if (url.isLocalFile()) {
298  value = QDir::toNativeSeparators(value);
299  }
300 #endif
301 
302  nameKey = QStringLiteral("Name%1").arg(i);
303  nameValue = cg.readPathEntry(nameKey, url.fileName());
304  title = titleWithSensibleWidth(nameValue, value);
305  if (!value.isNull()) {
306  thereAreEntries = true;
307  addAction(new QAction(title, selectableActionGroup()), url, nameValue);
308  }
309  }
310  if (thereAreEntries) {
311  d->m_noEntriesAction->setVisible(false);
312  d->clearSeparator->setVisible(true);
313  d->clearAction->setVisible(true);
314  setEnabled(true);
315  }
316 }
317 
319 {
320  Q_D(KRecentFilesAction);
321  QString key;
322  QString value;
323  QStringList lst = items();
324 
325  KConfigGroup cg = _cg;
326  if (cg.name().isEmpty()) {
327  cg = KConfigGroup(cg.config(), "RecentFiles");
328  }
329 
330  cg.deleteGroup();
331 
332  // write file list
333  for (int i = 1; i <= selectableActionGroup()->actions().count(); i++) {
334  key = QStringLiteral("File%1").arg(i);
335  // i - 1 because we started from 1
336  value = d->m_urls[selectableActionGroup()->actions()[i - 1]].toDisplayString(QUrl::PreferLocalFile);
337  cg.writePathEntry(key, value);
338  key = QStringLiteral("Name%1").arg(i);
339  value = d->m_shortNames[selectableActionGroup()->actions()[i - 1]];
340  cg.writePathEntry(key, value);
341  }
342 }
343 
344 #include "moc_krecentfilesaction.cpp"
QString text() const const
PreferLocalFile
void setMaxItems(int maxItems)
Sets the maximum of items in the recent files list.
QString readPathEntry(const QString &pKey, const QString &aDefault) const
QString toNativeSeparators(const QString &pathName)
QString toDisplayString(QUrl::FormattingOptions options) const const
void writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags=Normal)
int maxItems() const
Returns the maximum of items in the recent files list.
QMap::const_iterator constBegin() const const
QIcon icon() const const
bool exists() const const
void addAction(QAction *action, const QUrl &url, const QString &name)
Adds action to the list of URLs, with url and title name.
void deleteGroup(WriteConfigFlags pFlags=Normal)
QUrl fromUserInput(const QString &userInput)
~KRecentFilesAction() override
Destructor.
virtual QAction * removeAction(QAction *action)
void urlSelected(const QUrl &url)
This signal gets emitted when the user selects an URL.
bool isNull() const const
void insertAction(QAction *before, QAction *action)
int count(const T &value) const const
void recentListCleared()
This signal gets emitted when the user clear list.
void saveEntries(const KConfigGroup &config)
Saves the current recent files entries to a given KConfigGroup object.
QString tempPath()
QStringList items() const
CaseInsensitive
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
void deleteLater()
QList< QAction * > actions() const
QString toLocalFile() const const
QList< QScreen * > screens()
QString name() const
KRecentFilesAction(QObject *parent)
Constructs an action with the specified parent.
virtual void clear()
Clears the recent files list.
QActionGroup * selectableActionGroup() const
QString i18n(const char *text, const TYPE &arg...)
QList< QAction * > actions() const const
KConfig * config()
QAction * action(int index) const
Recent files action.
void addUrl(const QUrl &url, const QString &name=QString())
Add URL to the recent files list.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
ElideMiddle
QAction(QObject *parent)
QMenu * menu() const const
QObject * parent() const const
QList< QUrl > urls() const
Retrieve a list of all URLs in the recent files list.
void triggered(QAction *action)
QString fileName(QUrl::ComponentFormattingOptions options) const const
void setEnabled(bool)
void removeUrl(const QUrl &url)
Remove an URL from the recent files list.
Q_EMITQ_EMIT
void loadEntries(const KConfigGroup &config)
Loads the recent files entries from a given KConfigGroup object.
QAction * removeAction(QAction *action) override
Reimplemented for internal reasons.
bool isLocalFile() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 23 2021 22:45:47 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.