Sync translations from Transifex and run lupdate
[qBittorrent.git] / src / gui / fspathedit_p.cpp
blobf2c8c294a1571c3a375db8fc202d48db2af49ea8
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_p.h"
31 #include <QCompleter>
32 #include <QContextMenuEvent>
33 #include <QDir>
34 #include <QFileInfo>
35 #include <QFileSystemModel>
36 #include <QMenu>
37 #include <QStringList>
38 #include <QStyle>
40 // -------------------- FileSystemPathValidator ----------------------------------------
41 Private::FileSystemPathValidator::FileSystemPathValidator(QObject *parent)
42 : QValidator(parent)
43 , m_strictMode(false)
44 , m_existingOnly(false)
45 , m_directoriesOnly(false)
46 , m_checkReadPermission(false)
47 , m_checkWritePermission(false)
48 , m_lastTestResult(TestResult::DoesNotExist)
49 , m_lastValidationState(QValidator::Invalid)
53 bool Private::FileSystemPathValidator::strictMode() const
55 return m_strictMode;
58 void Private::FileSystemPathValidator::setStrictMode(bool v)
60 m_strictMode = v;
63 bool Private::FileSystemPathValidator::existingOnly() const
65 return m_existingOnly;
68 void Private::FileSystemPathValidator::setExistingOnly(bool v)
70 m_existingOnly = v;
73 bool Private::FileSystemPathValidator::directoriesOnly() const
75 return m_directoriesOnly;
78 void Private::FileSystemPathValidator::setDirectoriesOnly(bool v)
80 m_directoriesOnly = v;
83 bool Private::FileSystemPathValidator::checkReadPermission() const
85 return m_checkReadPermission;
88 void Private::FileSystemPathValidator::setCheckReadPermission(bool v)
90 m_checkReadPermission = v;
93 bool Private::FileSystemPathValidator::checkWritePermission() const
95 return m_checkWritePermission;
98 void Private::FileSystemPathValidator::setCheckWritePermission(bool v)
100 m_checkWritePermission = v;
103 QValidator::State Private::FileSystemPathValidator::validate(QString &input, int &pos) const
105 if (input.isEmpty())
106 return m_strictMode ? QValidator::Invalid : QValidator::Intermediate;
108 // we test path components from beginning to the one with cursor location in strict mode
109 // and the one with cursor and beyond in non-strict mode
110 QList<QStringView> components = QStringView(input).split(QDir::separator(), Qt::KeepEmptyParts);
111 // find index of the component that contains pos
112 int componentWithCursorIndex = 0;
113 int componentWithCursorPosition = 0;
114 int pathLength = 0;
116 // components.size() - 1 because when path ends with QDir::separator(), we will not see the last
117 // character in the components array, yet everything past the one before the last delimiter
118 // belongs to the last component
119 for (; (componentWithCursorIndex < (components.size() - 1)) && (pathLength < pos); ++componentWithCursorIndex)
121 pathLength = componentWithCursorPosition + components[componentWithCursorIndex].size();
122 componentWithCursorPosition += components[componentWithCursorIndex].size() + 1;
125 Q_ASSERT(componentWithCursorIndex < components.size());
127 m_lastValidationState = QValidator::Acceptable;
128 if (componentWithCursorIndex > 0)
129 m_lastValidationState = validate(components, m_strictMode, 0, componentWithCursorIndex - 1);
130 if ((m_lastValidationState == QValidator::Acceptable) && (componentWithCursorIndex < components.size()))
131 m_lastValidationState = validate(components, false, componentWithCursorIndex, components.size() - 1);
132 return m_lastValidationState;
135 QValidator::State Private::FileSystemPathValidator::validate(const QList<QStringView> &pathComponents, bool strict,
136 int firstComponentToTest, int lastComponentToTest) const
138 Q_ASSERT(firstComponentToTest >= 0);
139 Q_ASSERT(lastComponentToTest >= firstComponentToTest);
140 Q_ASSERT(lastComponentToTest < pathComponents.size());
142 m_lastTestResult = TestResult::DoesNotExist;
143 if (pathComponents.empty())
144 return strict ? QValidator::Invalid : QValidator::Intermediate;
146 for (int i = firstComponentToTest; i <= lastComponentToTest; ++i)
148 const bool isFinalPath = (i == (pathComponents.size() - 1));
149 const QStringView componentPath = pathComponents[i];
150 if (componentPath.isEmpty()) continue;
152 m_lastTestResult = testPath(pathComponents[i], isFinalPath);
153 if (m_lastTestResult != TestResult::OK)
155 m_lastTestedPath = componentPath.toString();
156 return strict ? QValidator::Invalid : QValidator::Intermediate;
160 return QValidator::Acceptable;
163 Private::FileSystemPathValidator::TestResult
164 Private::FileSystemPathValidator::testPath(const QStringView path, bool pathIsComplete) const
166 QFileInfo fi(path.toString());
167 if (m_existingOnly && !fi.exists())
168 return TestResult::DoesNotExist;
170 if ((!pathIsComplete || m_directoriesOnly) && !fi.isDir())
171 return TestResult::NotADir;
173 if (pathIsComplete)
175 if (!m_directoriesOnly && fi.isDir())
176 return TestResult::NotAFile;
178 if (m_checkWritePermission && (fi.exists() && !fi.isWritable()))
179 return TestResult::CantWrite;
180 if (m_checkReadPermission && !fi.isReadable())
181 return TestResult::CantRead;
184 return TestResult::OK;
187 Private::FileSystemPathValidator::TestResult Private::FileSystemPathValidator::lastTestResult() const
189 return m_lastTestResult;
192 QValidator::State Private::FileSystemPathValidator::lastValidationState() const
194 return m_lastValidationState;
197 QString Private::FileSystemPathValidator::lastTestedPath() const
199 return m_lastTestedPath;
202 Private::FileLineEdit::FileLineEdit(QWidget *parent)
203 : QLineEdit {parent}
204 , m_completerModel {new QFileSystemModel(this)}
205 , m_completer {new QCompleter(this)}
206 , m_browseAction {nullptr}
207 , m_warningAction {nullptr}
209 m_completerModel->setRootPath("");
210 m_completerModel->setIconProvider(&m_iconProvider);
211 m_completer->setModel(m_completerModel);
212 m_completer->setCompletionMode(QCompleter::PopupCompletion);
213 setCompleter(m_completer);
216 Private::FileLineEdit::~FileLineEdit()
218 delete m_completerModel; // has to be deleted before deleting the m_iconProvider object
221 void Private::FileLineEdit::completeDirectoriesOnly(bool completeDirsOnly)
223 QDir::Filters filters = completeDirsOnly ? QDir::Dirs : QDir::AllEntries;
224 filters |= QDir::NoDotAndDotDot;
225 m_completerModel->setFilter(filters);
228 void Private::FileLineEdit::setFilenameFilters(const QStringList &filters)
230 m_completerModel->setNameFilters(filters);
233 void Private::FileLineEdit::setBrowseAction(QAction *action)
235 m_browseAction = action;
238 void Private::FileLineEdit::setValidator(QValidator *validator)
240 QLineEdit::setValidator(validator);
243 QWidget *Private::FileLineEdit::widget()
245 return this;
248 void Private::FileLineEdit::keyPressEvent(QKeyEvent *e)
250 QLineEdit::keyPressEvent(e);
251 if ((e->key() == Qt::Key_Space) && (e->modifiers() == Qt::CTRL))
253 m_completerModel->setRootPath(QFileInfo(text()).absoluteDir().absolutePath());
254 showCompletionPopup();
257 auto *validator = qobject_cast<const FileSystemPathValidator *>(this->validator());
258 if (validator)
260 FileSystemPathValidator::TestResult lastTestResult = validator->lastTestResult();
261 QValidator::State lastState = validator->lastValidationState();
262 if (lastTestResult == FileSystemPathValidator::TestResult::OK)
264 delete m_warningAction;
265 m_warningAction = nullptr;
267 else
269 if (!m_warningAction)
271 m_warningAction = new QAction(this);
272 addAction(m_warningAction, QLineEdit::TrailingPosition);
276 if (m_warningAction)
278 if (lastState == QValidator::Invalid)
279 m_warningAction->setIcon(style()->standardIcon(QStyle::SP_MessageBoxCritical));
280 else if (lastState == QValidator::Intermediate)
281 m_warningAction->setIcon(style()->standardIcon(QStyle::SP_MessageBoxWarning));
282 m_warningAction->setToolTip(warningText(lastTestResult).arg(validator->lastTestedPath()));
287 void Private::FileLineEdit::contextMenuEvent(QContextMenuEvent *event)
289 QMenu *menu = createStandardContextMenu();
290 menu->setAttribute(Qt::WA_DeleteOnClose);
292 if (m_browseAction)
294 menu->addSeparator();
295 menu->addAction(m_browseAction);
298 menu->popup(event->globalPos());
301 void Private::FileLineEdit::showCompletionPopup()
303 m_completer->setCompletionPrefix(text());
304 m_completer->complete();
307 QString Private::FileLineEdit::warningText(FileSystemPathValidator::TestResult r)
309 using TestResult = FileSystemPathValidator::TestResult;
310 switch (r)
312 case TestResult::DoesNotExist:
313 return tr("'%1' does not exist");
314 case TestResult::NotADir:
315 return tr("'%1' does not point to a directory");
316 case TestResult::NotAFile:
317 return tr("'%1' does not point to a file");
318 case TestResult::CantRead:
319 return tr("Does not have read permission in '%1'");
320 case TestResult::CantWrite:
321 return tr("Does not have write permission in '%1'");
322 default:
323 return {};
327 Private::FileComboEdit::FileComboEdit(QWidget *parent)
328 : QComboBox {parent}
330 setEditable(true);
331 setLineEdit(new FileLineEdit(this));
334 void Private::FileComboEdit::completeDirectoriesOnly(bool completeDirsOnly)
336 static_cast<FileLineEdit *>(lineEdit())->completeDirectoriesOnly(completeDirsOnly);
339 void Private::FileComboEdit::setBrowseAction(QAction *action)
341 static_cast<FileLineEdit *>(lineEdit())->setBrowseAction(action);
344 void Private::FileComboEdit::setValidator(QValidator *validator)
346 lineEdit()->setValidator(validator);
349 void Private::FileComboEdit::setFilenameFilters(const QStringList &filters)
351 static_cast<FileLineEdit *>(lineEdit())->setFilenameFilters(filters);
354 QWidget *Private::FileComboEdit::widget()
356 return this;
359 QString Private::FileComboEdit::text() const
361 return currentText();