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"
32 #include <QContextMenuEvent>
35 #include <QFileSystemModel>
37 #include <QStringList>
40 // -------------------- FileSystemPathValidator ----------------------------------------
41 Private::FileSystemPathValidator::FileSystemPathValidator(QObject
*parent
)
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
58 void Private::FileSystemPathValidator::setStrictMode(bool v
)
63 bool Private::FileSystemPathValidator::existingOnly() const
65 return m_existingOnly
;
68 void Private::FileSystemPathValidator::setExistingOnly(bool 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
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;
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
;
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
)
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()
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());
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;
269 if (!m_warningAction
)
271 m_warningAction
= new QAction(this);
272 addAction(m_warningAction
, QLineEdit::TrailingPosition
);
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
);
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
;
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'");
327 Private::FileComboEdit::FileComboEdit(QWidget
*parent
)
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()
359 QString
Private::FileComboEdit::text() const
361 return currentText();