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, SIGNAL(triggered(QAction *)), SLOT(_k_urlSelected(QAction *)));
79 }
80 
82 
83 void KRecentFilesActionPrivate::_k_urlSelected(QAction *action)
84 {
85  Q_Q(KRecentFilesAction);
86  Q_EMIT q->urlSelected(m_urls[action]);
87 }
88 
90 {
91  Q_D(const KRecentFilesAction);
92  return d->m_maxItems;
93 }
94 
96 {
97  Q_D(KRecentFilesAction);
98  // set new maxItems
99  d->m_maxItems = std::max(maxItems, 0);
100 
101  // remove all excess items
102  while (selectableActionGroup()->actions().count() > d->m_maxItems) {
103  delete removeAction(selectableActionGroup()->actions().last());
104  }
105 }
106 
107 static QString titleWithSensibleWidth(const QString &nameValue, const QString &value)
108 {
109  // Calculate 3/4 of screen geometry, we do not want
110  // action titles to be bigger than that
111  // Since we do not know in which screen we are going to show
112  // we choose the min of all the screens
113  int maxWidthForTitles = INT_MAX;
114  const auto screens = QGuiApplication::screens();
115  for (QScreen *screen : screens) {
116  maxWidthForTitles = qMin(maxWidthForTitles, screen->availableGeometry().width() * 3 / 4);
117  }
118  const QFontMetrics fontMetrics = QFontMetrics(QFont());
119 
120  QString title = nameValue + QLatin1String(" [") + value + QLatin1Char(']');
121  const int nameWidth = fontMetrics.boundingRect(title).width();
122  if (nameWidth > maxWidthForTitles) {
123  // If it does not fit, try to cut only the whole path, though if the
124  // name is too long (more than 3/4 of the whole text) we cut it a bit too
125  const int nameValueMaxWidth = maxWidthForTitles * 3 / 4;
126  QString cutNameValue, cutValue;
127  if (nameWidth > nameValueMaxWidth) {
128  cutNameValue = fontMetrics.elidedText(nameValue, Qt::ElideMiddle, nameValueMaxWidth);
129  cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameValueMaxWidth);
130  } else {
131  cutNameValue = nameValue;
132  cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameWidth);
133  }
134  title = cutNameValue + QLatin1String(" [") + cutValue + QLatin1Char(']');
135  }
136  return title;
137 }
138 
139 void KRecentFilesAction::addUrl(const QUrl &_url, const QString &name)
140 {
141  Q_D(KRecentFilesAction);
142 
143  // ensure we never add items if we want none
144  if (d->m_maxItems == 0) {
145  return;
146  }
147 
148  /**
149  * Create a deep copy here, because if _url is the parameter from
150  * urlSelected() signal, we will delete it in the removeAction() call below.
151  * but access it again in the addAction call... => crash
152  */
153  const QUrl url(_url);
154 
155  if (url.isLocalFile() && url.toLocalFile().startsWith(QDir::tempPath())) {
156  return;
157  }
158  const QString tmpName = name.isEmpty() ? url.fileName() : name;
159  const QString pathOrUrl(url.toDisplayString(QUrl::PreferLocalFile));
160 
161 #ifdef Q_OS_WIN
162  const QString file = url.isLocalFile() ? QDir::toNativeSeparators(pathOrUrl) : pathOrUrl;
163 #else
164  const QString file = pathOrUrl;
165 #endif
166 
167  // remove file if already in list
168  const auto lstActions = selectableActionGroup()->actions();
169  for (QAction *action : lstActions) {
170  const QString urlStr = d->m_urls[action].toDisplayString(QUrl::PreferLocalFile);
171 #ifdef Q_OS_WIN
172  const QString tmpFileName = url.isLocalFile() ? QDir::toNativeSeparators(urlStr) : urlStr;
173  if (tmpFileName.endsWith(file, Qt::CaseInsensitive))
174 #else
175  if (urlStr.endsWith(file))
176 #endif
177  {
178  removeAction(action)->deleteLater();
179  break;
180  }
181  }
182  // remove oldest item if already maxitems in list
183  Q_ASSERT(d->m_maxItems > 0);
184  if (selectableActionGroup()->actions().count() == d->m_maxItems) {
185  // remove oldest added item
186  delete removeAction(selectableActionGroup()->actions().first());
187  }
188 
189  d->m_noEntriesAction->setVisible(false);
190  d->clearSeparator->setVisible(true);
191  d->clearAction->setVisible(true);
192  setEnabled(true);
193  // add file to list
194  const QString title = titleWithSensibleWidth(tmpName, file);
195  QAction *action = new QAction(title, selectableActionGroup());
196  addAction(action, url, tmpName);
197 }
198 
199 void KRecentFilesAction::addAction(QAction *action, const QUrl &url, const QString &name)
200 {
201  Q_D(KRecentFilesAction);
202 
203  menu()->insertAction(menu()->actions().value(0), action);
204  d->m_shortNames.insert(action, name);
205  d->m_urls.insert(action, url);
206 }
207 
209 {
210  Q_D(KRecentFilesAction);
212 
213  d->m_shortNames.remove(action);
214  d->m_urls.remove(action);
215 
216  return action;
217 }
218 
220 {
221  Q_D(KRecentFilesAction);
222  for (QMap<QAction *, QUrl>::ConstIterator it = d->m_urls.constBegin(); it != d->m_urls.constEnd(); ++it)
223  if (it.value() == url) {
224  delete removeAction(it.key());
225  return;
226  }
227 }
228 
230 {
231  Q_D(const KRecentFilesAction);
232  return d->m_urls.values();
233 }
234 
236 {
237  clearEntries();
239 }
240 
241 void KRecentFilesAction::clearEntries()
242 {
243  Q_D(KRecentFilesAction);
245  d->m_shortNames.clear();
246  d->m_urls.clear();
247  d->m_noEntriesAction->setVisible(true);
248  d->clearSeparator->setVisible(false);
249  d->clearAction->setVisible(false);
250  setEnabled(false);
251 }
252 
254 {
255  Q_D(KRecentFilesAction);
256  clearEntries();
257 
258  QString key;
259  QString value;
260  QString nameKey;
261  QString nameValue;
262  QString title;
263  QUrl url;
264 
265  KConfigGroup cg = _config;
266  if (cg.name().isEmpty()) {
267  cg = KConfigGroup(cg.config(), "RecentFiles");
268  }
269 
270  bool thereAreEntries = false;
271  // read file list
272  for (int i = 1; i <= d->m_maxItems; i++) {
273  key = QStringLiteral("File%1").arg(i);
274  value = cg.readPathEntry(key, QString());
275  if (value.isEmpty()) {
276  continue;
277  }
278  url = QUrl::fromUserInput(value);
279 
280  // Don't restore if file doesn't exist anymore
281  if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) {
282  continue;
283  }
284 
285  // Don't restore where the url is already known (eg. broken config)
286  if (d->m_urls.values().contains(url)) {
287  continue;
288  }
289 
290 #ifdef Q_OS_WIN
291  // convert to backslashes
292  if (url.isLocalFile()) {
293  value = QDir::toNativeSeparators(value);
294  }
295 #endif
296 
297  nameKey = QStringLiteral("Name%1").arg(i);
298  nameValue = cg.readPathEntry(nameKey, url.fileName());
299  title = titleWithSensibleWidth(nameValue, value);
300  if (!value.isNull()) {
301  thereAreEntries = true;
302  addAction(new QAction(title, selectableActionGroup()), url, nameValue);
303  }
304  }
305  if (thereAreEntries) {
306  d->m_noEntriesAction->setVisible(false);
307  d->clearSeparator->setVisible(true);
308  d->clearAction->setVisible(true);
309  setEnabled(true);
310  }
311 }
312 
314 {
315  Q_D(KRecentFilesAction);
316  QString key;
317  QString value;
318  QStringList lst = items();
319 
320  KConfigGroup cg = _cg;
321  if (cg.name().isEmpty()) {
322  cg = KConfigGroup(cg.config(), "RecentFiles");
323  }
324 
325  cg.deleteGroup();
326 
327  // write file list
328  for (int i = 1; i <= selectableActionGroup()->actions().count(); i++) {
329  key = QStringLiteral("File%1").arg(i);
330  // i - 1 because we started from 1
331  value = d->m_urls[selectableActionGroup()->actions()[i - 1]].toDisplayString(QUrl::PreferLocalFile);
332  cg.writePathEntry(key, value);
333  key = QStringLiteral("Name%1").arg(i);
334  value = d->m_shortNames[selectableActionGroup()->actions()[i - 1]];
335  cg.writePathEntry(key, value);
336  }
337 }
338 
339 #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)
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 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 Wed May 12 2021 22:43:43 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.