Provide torrent creation feature via WebAPI
[qBittorrent.git] / src / gui / fspathedit.cpp
blobff3a8f1e450b131e9adb8df6db6a41b0322039f1
1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2016 Eugene Shalygin
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.
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.
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.
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.
29 #include "fspathedit.h"
31 #include <memory>
32 #include <stdexcept>
34 #include <QAction>
35 #include <QApplication>
36 #include <QCoreApplication>
37 #include <QFileDialog>
38 #include <QHBoxLayout>
39 #include <QStyle>
40 #include <QToolButton>
42 #include "base/utils/fs.h"
43 #include "fspathedit_p.h"
45 namespace
47 struct TrStringWithComment
49 const char *source = nullptr;
50 const char *comment = nullptr;
52 QString tr() const
54 return QCoreApplication::translate("FileSystemPathEdit", source, comment);
58 constexpr TrStringWithComment browseButtonBriefText =
59 QT_TRANSLATE_NOOP3("FileSystemPathEdit", "...", "Launch file dialog button text (brief)");
60 constexpr TrStringWithComment browseButtonFullText =
61 QT_TRANSLATE_NOOP3("FileSystemPathEdit", "&Browse...", "Launch file dialog button text (full)");
62 constexpr TrStringWithComment defaultDialogCaptionForFile =
63 QT_TRANSLATE_NOOP3("FileSystemPathEdit", "Choose a file", "Caption for file open/save dialog");
64 constexpr TrStringWithComment defaultDialogCaptionForDirectory =
65 QT_TRANSLATE_NOOP3("FileSystemPathEdit", "Choose a folder", "Caption for directory open dialog");
68 class FileSystemPathEdit::FileSystemPathEditPrivate
70 Q_DECLARE_PUBLIC(FileSystemPathEdit)
71 Q_DISABLE_COPY_MOVE(FileSystemPathEditPrivate)
73 private:
74 FileSystemPathEditPrivate(FileSystemPathEdit *q, Private::IFileEditorWithCompletion *editor);
76 void modeChanged();
77 void browseActionTriggered();
78 QString dialogCaptionOrDefault() const;
80 FileSystemPathEdit *q_ptr = nullptr;
81 std::unique_ptr<Private::IFileEditorWithCompletion> m_editor;
82 QAction *m_browseAction = nullptr;
83 QToolButton *m_browseBtn = nullptr;
84 QString m_fileNameFilter;
85 Mode m_mode = FileSystemPathEdit::Mode::FileOpen;
86 Path m_lastSignaledPath;
87 QString m_dialogCaption;
88 Private::FileSystemPathValidator *m_validator = nullptr;
91 FileSystemPathEdit::FileSystemPathEditPrivate::FileSystemPathEditPrivate(
92 FileSystemPathEdit *q, Private::IFileEditorWithCompletion *editor)
93 : q_ptr {q}
94 , m_editor {editor}
95 , m_browseAction {new QAction(QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon), browseButtonFullText.tr(), q)}
96 , m_browseBtn {new QToolButton(q)}
97 , m_fileNameFilter {tr("Any file") + u" (*)"}
98 , m_validator {new Private::FileSystemPathValidator(q)}
100 m_browseAction->setIconText(browseButtonBriefText.tr());
101 m_browseAction->setShortcut(Qt::CTRL | Qt::Key_B);
102 m_browseAction->setToolTip(browseButtonFullText.tr().remove(u'&'));
104 m_browseBtn->setDefaultAction(m_browseAction);
106 m_validator->setStrictMode(false);
108 m_editor->setBrowseAction(m_browseAction);
109 m_editor->setValidator(m_validator);
111 modeChanged();
114 void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
116 Q_Q(FileSystemPathEdit);
118 const Path currentDirectory = (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave)
119 ? q->selectedPath()
120 : q->selectedPath().parentPath();
121 const Path initialDirectory = currentDirectory.isAbsolute() ? currentDirectory : (Utils::Fs::homePath() / currentDirectory);
123 QString filter = q->fileNameFilter();
124 QString newPath;
125 switch (m_mode)
127 case FileSystemPathEdit::Mode::FileOpen:
128 newPath = QFileDialog::getOpenFileName(q, dialogCaptionOrDefault(), initialDirectory.data(), filter);
129 break;
130 case FileSystemPathEdit::Mode::FileSave:
131 newPath = QFileDialog::getSaveFileName(q, dialogCaptionOrDefault(), initialDirectory.data(), filter, &filter);
132 break;
133 case FileSystemPathEdit::Mode::DirectoryOpen:
134 case FileSystemPathEdit::Mode::DirectorySave:
135 newPath = QFileDialog::getExistingDirectory(q, dialogCaptionOrDefault(),
136 initialDirectory.data(), QFileDialog::ShowDirsOnly);
137 break;
138 case FileSystemPathEdit::Mode::ReadOnly:
139 throw std::logic_error("Not supported");
140 default:
141 throw std::logic_error("Unknown FileSystemPathEdit mode");
144 if (!newPath.isEmpty())
145 q->setSelectedPath(Path(newPath));
148 QString FileSystemPathEdit::FileSystemPathEditPrivate::dialogCaptionOrDefault() const
150 if (!m_dialogCaption.isEmpty())
151 return m_dialogCaption;
153 switch (m_mode)
155 case FileSystemPathEdit::Mode::FileOpen:
156 case FileSystemPathEdit::Mode::FileSave:
157 return defaultDialogCaptionForFile.tr();
158 case FileSystemPathEdit::Mode::DirectoryOpen:
159 case FileSystemPathEdit::Mode::DirectorySave:
160 return defaultDialogCaptionForDirectory.tr();
161 case FileSystemPathEdit::Mode::ReadOnly:
162 throw std::logic_error("Not supported");
163 default:
164 throw std::logic_error("Unknown FileSystemPathEdit mode");
168 void FileSystemPathEdit::FileSystemPathEditPrivate::modeChanged()
170 m_browseBtn->setVisible(m_mode != FileSystemPathEdit::Mode::ReadOnly);
171 m_editor->completeDirectoriesOnly((m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave));
173 m_validator->setExistingOnly((m_mode == FileSystemPathEdit::Mode::FileOpen) || (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::ReadOnly));
174 m_validator->setFilesOnly((m_mode == FileSystemPathEdit::Mode::FileOpen) || (m_mode == FileSystemPathEdit::Mode::FileSave));
175 m_validator->setDirectoriesOnly((m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave));
176 m_validator->setCheckReadPermission((m_mode == FileSystemPathEdit::Mode::FileOpen) || (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::ReadOnly));
177 m_validator->setCheckWritePermission((m_mode == FileSystemPathEdit::Mode::FileSave) || (m_mode == FileSystemPathEdit::Mode::DirectorySave));
180 FileSystemPathEdit::FileSystemPathEdit(Private::IFileEditorWithCompletion *editor, QWidget *parent)
181 : QWidget(parent)
182 , d_ptr(new FileSystemPathEditPrivate(this, editor))
184 Q_D(FileSystemPathEdit);
185 editor->widget()->setParent(this);
187 setFocusProxy(editor->widget());
188 // required, otherwise the button cannot be selected via keyboard tab
189 setTabOrder(editor->widget(), d_ptr->m_browseBtn);
191 auto *layout = new QHBoxLayout(this);
192 layout->setContentsMargins(0, 0, 0, 0);
193 layout->addWidget(editor->widget());
194 layout->addWidget(d->m_browseBtn);
196 connect(d->m_browseAction, &QAction::triggered, this, [this]() { this->d_func()->browseActionTriggered(); });
199 FileSystemPathEdit::~FileSystemPathEdit()
201 delete d_ptr;
204 Path FileSystemPathEdit::selectedPath() const
206 return Path(editWidgetText());
209 void FileSystemPathEdit::setSelectedPath(const Path &val)
211 Q_D(FileSystemPathEdit);
213 const QString nativePath = val.toString();
214 setEditWidgetText(nativePath);
215 d->m_editor->widget()->setToolTip(nativePath);
218 QString FileSystemPathEdit::fileNameFilter() const
220 Q_D(const FileSystemPathEdit);
221 return d->m_fileNameFilter;
224 void FileSystemPathEdit::setFileNameFilter(const QString &val)
226 Q_D(FileSystemPathEdit);
227 d->m_fileNameFilter = val;
229 #if 0
230 // QFileSystemModel applies name filters to directories too.
231 // To use the filters we have to subclass QFileSystemModel and skip directories while filtering
232 // extract file masks
233 const int openBracePos = val.indexOf(u'(');
234 const int closeBracePos = val.indexOf(u')', (openBracePos + 1));
235 if ((openBracePos > 0) && (closeBracePos > 0) && (closeBracePos > openBracePos + 2))
237 QString filterString = val.mid(openBracePos + 1, closeBracePos - openBracePos - 1);
238 if (filterString == u"*")
239 { // no filters
240 d->m_editor->setFilenameFilters({});
242 else
244 QStringList filters = filterString.split(u' ', Qt::SkipEmptyParts);
245 d->m_editor->setFilenameFilters(filters);
248 else
250 d->m_editor->setFilenameFilters({});
252 #endif
255 Path FileSystemPathEdit::placeholder() const
257 Q_D(const FileSystemPathEdit);
258 return d->m_editor->placeholder();
261 void FileSystemPathEdit::setPlaceholder(const Path &val)
263 Q_D(FileSystemPathEdit);
264 d->m_editor->setPlaceholder(val);
267 bool FileSystemPathEdit::briefBrowseButtonCaption() const
269 Q_D(const FileSystemPathEdit);
270 return d->m_browseBtn->text() == browseButtonBriefText.tr();
273 void FileSystemPathEdit::setBriefBrowseButtonCaption(bool brief)
275 Q_D(FileSystemPathEdit);
276 d->m_browseBtn->setText(brief ? browseButtonBriefText.tr() : browseButtonFullText.tr());
279 void FileSystemPathEdit::onPathEdited()
281 Q_D(FileSystemPathEdit);
283 const Path newPath = selectedPath();
284 if (newPath != d->m_lastSignaledPath)
286 emit selectedPathChanged(newPath);
287 d->m_lastSignaledPath = newPath;
288 d->m_editor->widget()->setToolTip(editWidgetText());
292 FileSystemPathEdit::Mode FileSystemPathEdit::mode() const
294 Q_D(const FileSystemPathEdit);
295 return d->m_mode;
298 void FileSystemPathEdit::setMode(const Mode mode)
300 Q_D(FileSystemPathEdit);
301 d->m_mode = mode;
302 d->modeChanged();
305 QString FileSystemPathEdit::dialogCaption() const
307 Q_D(const FileSystemPathEdit);
308 return d->m_dialogCaption;
311 void FileSystemPathEdit::setDialogCaption(const QString &caption)
313 Q_D(FileSystemPathEdit);
314 d->m_dialogCaption = caption;
317 QWidget *FileSystemPathEdit::editWidgetImpl() const
319 Q_D(const FileSystemPathEdit);
320 return d->m_editor->widget();
323 // ------------------------- FileSystemPathLineEdit ----------------------
324 FileSystemPathLineEdit::FileSystemPathLineEdit(QWidget *parent)
325 : FileSystemPathEdit(new WidgetType(), parent)
327 connect(editWidget<WidgetType>(), &QLineEdit::editingFinished, this, &FileSystemPathLineEdit::onPathEdited);
328 connect(editWidget<WidgetType>(), &QLineEdit::textChanged, this, &FileSystemPathLineEdit::onPathEdited);
331 QString FileSystemPathLineEdit::editWidgetText() const
333 return editWidget<WidgetType>()->text();
336 void FileSystemPathLineEdit::clear()
338 editWidget<WidgetType>()->clear();
341 void FileSystemPathLineEdit::setEditWidgetText(const QString &text)
343 editWidget<WidgetType>()->setText(text);
346 // ----------------------- FileSystemPathComboEdit -----------------------
347 FileSystemPathComboEdit::FileSystemPathComboEdit(QWidget *parent)
348 : FileSystemPathEdit(new WidgetType(), parent)
350 editWidget<WidgetType>()->setEditable(true);
351 connect(editWidget<WidgetType>(), &QComboBox::currentTextChanged, this, &FileSystemPathComboEdit::onPathEdited);
352 connect(editWidget<WidgetType>()->lineEdit(), &QLineEdit::editingFinished, this, &FileSystemPathComboEdit::onPathEdited);
355 void FileSystemPathComboEdit::clear()
357 editWidget<WidgetType>()->clear();
360 int FileSystemPathComboEdit::count() const
362 return editWidget<WidgetType>()->count();
365 Path FileSystemPathComboEdit::item(int index) const
367 return Path(editWidget<WidgetType>()->itemText(index));
370 void FileSystemPathComboEdit::addItem(const Path &path)
372 editWidget<WidgetType>()->addItem(path.toString());
375 void FileSystemPathComboEdit::insertItem(int index, const Path &path)
377 editWidget<WidgetType>()->insertItem(index, path.toString());
380 int FileSystemPathComboEdit::currentIndex() const
382 return editWidget<WidgetType>()->currentIndex();
385 void FileSystemPathComboEdit::setCurrentIndex(int index)
387 editWidget<WidgetType>()->setCurrentIndex(index);
390 int FileSystemPathComboEdit::maxVisibleItems() const
392 return editWidget<WidgetType>()->maxVisibleItems();
395 void FileSystemPathComboEdit::setMaxVisibleItems(int maxItems)
397 editWidget<WidgetType>()->setMaxVisibleItems(maxItems);
400 QString FileSystemPathComboEdit::editWidgetText() const
402 return editWidget<WidgetType>()->currentText();
405 void FileSystemPathComboEdit::setEditWidgetText(const QString &text)
407 editWidget<WidgetType>()->setCurrentText(text);