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"
35 #include <QApplication>
36 #include <QCoreApplication>
37 #include <QFileDialog>
38 #include <QHBoxLayout>
40 #include <QToolButton>
42 #include "base/utils/fs.h"
43 #include "fspathedit_p.h"
47 struct TrStringWithComment
49 const char *source
= nullptr;
50 const char *comment
= nullptr;
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
)
74 FileSystemPathEditPrivate(FileSystemPathEdit
*q
, Private::IFileEditorWithCompletion
*editor
);
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
)
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
);
114 void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
116 Q_Q(FileSystemPathEdit
);
118 const Path currentDirectory
= (m_mode
== FileSystemPathEdit::Mode::DirectoryOpen
) || (m_mode
== FileSystemPathEdit::Mode::DirectorySave
)
120 : q
->selectedPath().parentPath();
121 const Path initialDirectory
= currentDirectory
.isAbsolute() ? currentDirectory
: (Utils::Fs::homePath() / currentDirectory
);
123 QString filter
= q
->fileNameFilter();
127 case FileSystemPathEdit::Mode::FileOpen
:
128 newPath
= QFileDialog::getOpenFileName(q
, dialogCaptionOrDefault(), initialDirectory
.data(), filter
);
130 case FileSystemPathEdit::Mode::FileSave
:
131 newPath
= QFileDialog::getSaveFileName(q
, dialogCaptionOrDefault(), initialDirectory
.data(), filter
, &filter
);
133 case FileSystemPathEdit::Mode::DirectoryOpen
:
134 case FileSystemPathEdit::Mode::DirectorySave
:
135 newPath
= QFileDialog::getExistingDirectory(q
, dialogCaptionOrDefault(),
136 initialDirectory
.data(), QFileDialog::ShowDirsOnly
);
138 case FileSystemPathEdit::Mode::ReadOnly
:
139 throw std::logic_error("Not supported");
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
;
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");
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
)
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()
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
;
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
"*")
240 d
->m_editor
->setFilenameFilters({});
244 QStringList filters
= filterString
.split(u
' ', Qt::SkipEmptyParts
);
245 d
->m_editor
->setFilenameFilters(filters
);
250 d
->m_editor
->setFilenameFilters({});
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
);
298 void FileSystemPathEdit::setMode(const Mode mode
)
300 Q_D(FileSystemPathEdit
);
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
);