qBittorrent
fspathedit.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2016 Eugene Shalygin
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * In addition, as a special exception, the copyright holders give permission to
20  * link this program with the OpenSSL project's "OpenSSL" library (or with
21  * modified versions of it that use the same license as the "OpenSSL" library),
22  * and distribute the linked executables. You must obey the GNU General Public
23  * License in all respects for all of the code used other than "OpenSSL". If you
24  * modify file(s), you may extend this exception to your version of the file(s),
25  * but you are not obligated to do so. If you do not wish to do so, delete this
26  * exception statement from your version.
27  */
28 
29 #include "fspathedit.h"
30 
31 #include <memory>
32 #include <stdexcept>
33 
34 #include <QAction>
35 #include <QApplication>
36 #include <QCoreApplication>
37 #include <QFileDialog>
38 #include <QHBoxLayout>
39 #include <QStyle>
40 #include <QToolButton>
41 
42 #include "base/utils/fs.h"
43 #include "fspathedit_p.h"
44 
45 namespace
46 {
48  {
49  const char *source;
50  const char *comment;
51 
52  QString tr() const
53  {
54  return QCoreApplication::translate("FileSystemPathEdit", source, comment);
55  }
56  };
57 
59  QT_TRANSLATE_NOOP3("FileSystemPathEdit", "...", "Launch file dialog button text (brief)");
61  QT_TRANSLATE_NOOP3("FileSystemPathEdit", "&Browse...", "Launch file dialog button text (full)");
63  QT_TRANSLATE_NOOP3("FileSystemPathEdit", "Choose a file", "Caption for file open/save dialog");
65  QT_TRANSLATE_NOOP3("FileSystemPathEdit", "Choose a folder", "Caption for directory open dialog");
66 }
67 
69 {
70  Q_DECLARE_PUBLIC(FileSystemPathEdit)
71  Q_DISABLE_COPY_MOVE(FileSystemPathEditPrivate)
72 
74 
75  void modeChanged();
76  void browseActionTriggered();
77  QString dialogCaptionOrDefault() const;
78 
80  std::unique_ptr<Private::FileEditorWithCompletion> m_editor;
81  QAction *m_browseAction;
82  QToolButton *m_browseBtn;
86  QString m_dialogCaption;
88 };
89 
92  : q_ptr {q}
93  , m_editor {editor}
94  , m_browseAction {new QAction(q)}
95  , m_browseBtn {new QToolButton(q)}
96  , m_mode {FileSystemPathEdit::Mode::FileOpen}
97  , m_validator {new Private::FileSystemPathValidator(q)}
98 {
99  m_browseAction->setIconText(browseButtonBriefText.tr());
101  m_browseAction->setToolTip(browseButtonFullText.tr().remove(QLatin1Char('&')));
102  m_browseAction->setShortcut(Qt::CTRL + Qt::Key_B);
103  m_browseBtn->setDefaultAction(m_browseAction);
104  m_fileNameFilter = tr("Any file") + QLatin1String(" (*)");
105  m_editor->setBrowseAction(m_browseAction);
106  m_validator->setStrictMode(false);
107  m_editor->setValidator(m_validator);
108  modeChanged();
109 }
110 
112 {
113  Q_Q(FileSystemPathEdit);
114  QString filter = q->fileNameFilter();
115  QString directory = q->currentDirectory().isEmpty() ? QDir::homePath() : q->currentDirectory();
116 
117  QString selectedPath;
118  switch (m_mode)
119  {
121  selectedPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), directory, filter);
122  break;
124  selectedPath = QFileDialog::getSaveFileName(q, dialogCaptionOrDefault(), directory, filter, &filter);
125  break;
128  selectedPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(),
129  directory, QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly);
130  break;
131  default:
132  throw std::logic_error("Unknown FileSystemPathEdit mode");
133  }
134  if (!selectedPath.isEmpty())
135  q->setEditWidgetText(Utils::Fs::toNativePath(selectedPath));
136 }
137 
139 {
140  if (!m_dialogCaption.isEmpty())
141  return m_dialogCaption;
142 
143  switch (m_mode)
144  {
151  default:
152  throw std::logic_error("Unknown FileSystemPathEdit mode");
153  }
154 }
155 
157 {
158  bool showDirsOnly = false;
159  switch (m_mode)
160  {
163  showDirsOnly = false;
164  break;
167  showDirsOnly = true;
168  break;
169  default:
170  throw std::logic_error("Unknown FileSystemPathEdit mode");
171  }
172  m_browseAction->setIcon(QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon));
173  m_editor->completeDirectoriesOnly(showDirsOnly);
174 
175  m_validator->setExistingOnly(m_mode != FileSystemPathEdit::Mode::FileSave);
176  m_validator->setDirectoriesOnly((m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave));
177  m_validator->setCheckReadPermission((m_mode == FileSystemPathEdit::Mode::FileOpen) || (m_mode == FileSystemPathEdit::Mode::DirectoryOpen));
178  m_validator->setCheckWritePermission((m_mode == FileSystemPathEdit::Mode::FileSave) || (m_mode == FileSystemPathEdit::Mode::DirectorySave));
179 }
180 
182  : QWidget(parent)
183  , d_ptr(new FileSystemPathEditPrivate(this, editor))
184 {
185  Q_D(FileSystemPathEdit);
186  editor->widget()->setParent(this);
187 
188  auto *layout = new QHBoxLayout(this);
189  layout->setContentsMargins(0, 0, 0, 0);
190  layout->addWidget(editor->widget());
191  layout->addWidget(d->m_browseBtn);
192 
193  connect(d->m_browseAction, &QAction::triggered, this, [this]() { this->d_func()->browseActionTriggered(); });
194 }
195 
197 {
198  delete d_ptr;
199 }
200 
202 {
204 }
205 
206 void FileSystemPathEdit::setSelectedPath(const QString &val)
207 {
208  Q_D(FileSystemPathEdit);
210  d->m_editor->widget()->setToolTip(val);
211 }
212 
214 {
215  Q_D(const FileSystemPathEdit);
216  return d->m_fileNameFilter;
217 }
218 
220 {
221  Q_D(FileSystemPathEdit);
222  d->m_fileNameFilter = val;
223 
224 #if 0
225  // QFileSystemModel applies name filters to directories too.
226  // To use the filters we have to subclass QFileSystemModel and skip directories while filtering
227  // extract file masks
228  const int openBracePos = val.indexOf(QLatin1Char('('), 0);
229  const int closeBracePos = val.indexOf(QLatin1Char(')'), openBracePos + 1);
230  if ((openBracePos > 0) && (closeBracePos > 0) && (closeBracePos > openBracePos + 2))
231  {
232  QString filterString = val.mid(openBracePos + 1, closeBracePos - openBracePos - 1);
233  if (filterString == QLatin1String("*"))
234  { // no filters
235  d->m_editor->setFilenameFilters({});
236  }
237  else
238  {
239  QStringList filters = filterString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
240  d->m_editor->setFilenameFilters(filters);
241  }
242  }
243  else
244  {
245  d->m_editor->setFilenameFilters({});
246  }
247 #endif
248 }
249 
251 {
252  Q_D(const FileSystemPathEdit);
253  return d->m_editor->placeholder();
254 }
255 
256 void FileSystemPathEdit::setPlaceholder(const QString &val)
257 {
258  Q_D(FileSystemPathEdit);
259  d->m_editor->setPlaceholder(val);
260 }
261 
263 {
264  Q_D(const FileSystemPathEdit);
265  return d->m_browseBtn->text() == browseButtonBriefText.tr();
266 }
267 
269 {
270  Q_D(FileSystemPathEdit);
271  d->m_browseBtn->setText(brief ? browseButtonBriefText.tr() : browseButtonFullText.tr());
272 }
273 
275 {
276  Q_D(FileSystemPathEdit);
277  QString newPath = selectedPath();
278  if (newPath != d->m_lastSignaledPath)
279  {
280  emit selectedPathChanged(newPath);
281  d->m_lastSignaledPath = newPath;
282  d->m_editor->widget()->setToolTip(editWidgetText());
283  }
284 }
285 
287 {
288  Q_D(const FileSystemPathEdit);
289  return d->m_mode;
290 }
291 
293 {
294  Q_D(FileSystemPathEdit);
295  d->m_mode = theMode;
296  d->modeChanged();
297 }
298 
300 {
301  Q_D(const FileSystemPathEdit);
302  return d->m_dialogCaption;
303 }
304 
305 void FileSystemPathEdit::setDialogCaption(const QString &caption)
306 {
307  Q_D(FileSystemPathEdit);
308  d->m_dialogCaption = caption;
309 }
310 
312 {
313  return QFileInfo(selectedPath()).absoluteDir().absolutePath();
314 }
315 
317 {
318  Q_D(const FileSystemPathEdit);
319  return d->m_editor->widget();
320 }
321 
322 // ------------------------- FileSystemPathLineEdit ----------------------
324  : FileSystemPathEdit(new WidgetType(), parent)
325 {
326  connect(editWidget<WidgetType>(), &QLineEdit::editingFinished, this, &FileSystemPathLineEdit::onPathEdited);
327  connect(editWidget<WidgetType>(), &QLineEdit::textChanged, this, &FileSystemPathLineEdit::onPathEdited);
328 }
329 
331 {
332  return editWidget<WidgetType>()->text();
333 }
334 
336 {
337  editWidget<WidgetType>()->clear();
338 }
339 
341 {
342  editWidget<WidgetType>()->setText(text);
343 }
344 
345 // ----------------------- FileSystemPathComboEdit -----------------------
347  : FileSystemPathEdit(new WidgetType(), parent)
348 {
349  editWidget<WidgetType>()->setEditable(true);
350  connect(editWidget<WidgetType>(), &QComboBox::currentTextChanged, this, &FileSystemPathComboEdit::onPathEdited);
351  connect(editWidget<WidgetType>()->lineEdit(), &QLineEdit::editingFinished, this, &FileSystemPathComboEdit::onPathEdited);
352 }
353 
355 {
356  editWidget<WidgetType>()->clear();
357 }
358 
360 {
361  return editWidget<WidgetType>()->count();
362 }
363 
364 QString FileSystemPathComboEdit::item(int index) const
365 {
366  return Utils::Fs::toUniformPath(editWidget<WidgetType>()->itemText(index));
367 }
368 
369 void FileSystemPathComboEdit::addItem(const QString &text)
370 {
371  editWidget<WidgetType>()->addItem(Utils::Fs::toNativePath(text));
372 }
373 
374 void FileSystemPathComboEdit::insertItem(int index, const QString &text)
375 {
376  editWidget<WidgetType>()->insertItem(index, Utils::Fs::toNativePath(text));
377 }
378 
380 {
381  return editWidget<WidgetType>()->currentIndex();
382 }
383 
385 {
386  editWidget<WidgetType>()->setCurrentIndex(index);
387 }
388 
390 {
391  return editWidget<WidgetType>()->maxVisibleItems();
392 }
393 
395 {
396  editWidget<WidgetType>()->setMaxVisibleItems(maxItems);
397 }
398 
400 {
401  return editWidget<WidgetType>()->currentText();
402 }
403 
405 {
406  editWidget<WidgetType>()->setCurrentText(text);
407 }
FileSystemPathComboEdit(QWidget *parent=nullptr)
Definition: fspathedit.cpp:346
QString item(int index) const
Definition: fspathedit.cpp:364
void addItem(const QString &text)
Definition: fspathedit.cpp:369
void insertItem(int index, const QString &text)
Definition: fspathedit.cpp:374
void setEditWidgetText(const QString &text) override
Definition: fspathedit.cpp:404
void clear() override
Definition: fspathedit.cpp:354
int maxVisibleItems() const
Definition: fspathedit.cpp:389
void setCurrentIndex(int index)
Definition: fspathedit.cpp:384
QString editWidgetText() const override
Definition: fspathedit.cpp:399
void setMaxVisibleItems(int maxItems)
Definition: fspathedit.cpp:394
FileSystemPathEditPrivate(FileSystemPathEdit *q, Private::FileEditorWithCompletion *editor)
Definition: fspathedit.cpp:90
std::unique_ptr< Private::FileEditorWithCompletion > m_editor
Definition: fspathedit.cpp:80
Private::FileSystemPathValidator * m_validator
Definition: fspathedit.cpp:87
Widget for editing strings which are paths in filesystem.
Definition: fspathedit.h:45
FileSystemPathEditPrivate * d_ptr
Definition: fspathedit.h:107
virtual QString editWidgetText() const =0
QString dialogCaption
Definition: fspathedit.h:50
void setMode(Mode mode)
Definition: fspathedit.cpp:292
void setPlaceholder(const QString &val)
Definition: fspathedit.cpp:256
@ DirectoryOpen
selecting existing directories
@ FileOpen
opening files, shows open file dialog
@ FileSave
saving files, shows save file dialog
@ DirectorySave
selecting directories for saving
FileSystemPathEdit(Private::FileEditorWithCompletion *editor, QWidget *parent)
Definition: fspathedit.cpp:181
bool briefBrowseButtonCaption() const
The browse button caption is "..." if true, and "Browse" otherwise.
Definition: fspathedit.cpp:262
QString selectedPath
Definition: fspathedit.h:48
QWidget * editWidgetImpl() const
Definition: fspathedit.cpp:316
void setFileNameFilter(const QString &val)
Definition: fspathedit.cpp:219
virtual void setEditWidgetText(const QString &text)=0
void setDialogCaption(const QString &caption)
Definition: fspathedit.cpp:305
QString fileNameFilter
Definition: fspathedit.h:49
QString placeholder() const
Definition: fspathedit.cpp:250
void selectedPathChanged(const QString &path)
QString currentDirectory() const
Definition: fspathedit.cpp:311
void setSelectedPath(const QString &val)
Definition: fspathedit.cpp:206
void setBriefBrowseButtonCaption(bool brief)
Definition: fspathedit.cpp:268
~FileSystemPathEdit() override
Definition: fspathedit.cpp:196
void clear() override
Definition: fspathedit.cpp:335
QString editWidgetText() const override
Definition: fspathedit.cpp:330
void setEditWidgetText(const QString &text) override
Definition: fspathedit.cpp:340
FileSystemPathLineEdit(QWidget *parent=nullptr)
Definition: fspathedit.cpp:323
virtual QWidget * widget()=0
QString toUniformPath(const QString &path)
Definition: fs.cpp:69
QString toNativePath(const QString &path)
Definition: fs.cpp:64
constexpr TrStringWithComment browseButtonFullText
Definition: fspathedit.cpp:60
constexpr TrStringWithComment browseButtonBriefText
Definition: fspathedit.cpp:58
constexpr TrStringWithComment defaultDialogCaptionForFile
Definition: fspathedit.cpp:62
constexpr TrStringWithComment defaultDialogCaptionForDirectory
Definition: fspathedit.cpp:64