QFile: add supportsMoveToTrash()

[ChangeLog][QtCore][QFile] Added supportsMoveToTrash() to check if Qt
supports moving files to trash in the current OS.

Fixes: QTBUG-127580
Change-Id: Ifb754f0e28774c20aa7cfffd17e6c951ffd9d9ff
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This commit is contained in:
Thiago Macieira 2024-07-29 13:34:21 -07:00 committed by Ahmad Samir
parent 6faf18aceb
commit 17d1d577c9
7 changed files with 91 additions and 21 deletions

View File

@ -452,6 +452,22 @@ QFile::remove(const QString &fileName)
return QFile(fileName).remove(); return QFile(fileName).remove();
} }
/*!
\since 6.9
Returns \c true if Qt supports moving files to a trash (recycle bin) in the
current operating system using the moveToTrash() function, \c false
otherwise. Note that this function returning \c true does not imply
moveToTrash() will succeed. In particular, this function does not check if
the user has disabled the functionality in their settings.
\sa moveToTrash()
*/
bool QFile::supportsMoveToTrash()
{
return QFileSystemEngine::supportsMoveFileToTrash();
}
/*! /*!
\since 5.15 \since 5.15
@ -479,9 +495,12 @@ QFile::remove(const QString &fileName)
themselves mount points). themselves mount points).
//! [move-to-trash-common] //! [move-to-trash-common]
\note On systems where the system API doesn't report the location of the file in the \note On systems where the system API doesn't report the location of the
trash, fileName() will be set to the null string once the file has been moved. On file in the trash, fileName() will be set to the null string once the file
systems that don't have a trash can, this function always returns false. has been moved. On systems that don't have a trash can, this function
always returns \c false (see supportsMoveToTrash()).
\sa supportsMoveToTrash(), remove(), QDir::remove()
*/ */
bool bool
QFile::moveToTrash() QFile::moveToTrash()

View File

@ -210,6 +210,7 @@ public:
} }
#endif // QT_CONFIG(cxx17_filesystem) #endif // QT_CONFIG(cxx17_filesystem)
static bool supportsMoveToTrash() Q_DECL_PURE_FUNCTION;
bool moveToTrash(); bool moveToTrash();
static bool moveToTrash(const QString &fileName, QString *pathInTrash = nullptr); static bool moveToTrash(const QString &fileName, QString *pathInTrash = nullptr);
#ifdef Q_QDOC #ifdef Q_QDOC

View File

@ -20,6 +20,16 @@ QT_BEGIN_NAMESPACE
Using Finder would also play the trash sound, which we don't want either in Using Finder would also play the trash sound, which we don't want either in
such a core API; applications that want that can play the sound themselves. such a core API; applications that want that can play the sound themselves.
*/ */
//static
bool QFileSystemEngine::supportsMoveFileToTrash()
{
#ifdef Q_OS_MACOS // desktop macOS has a trash can
return true;
#else // watch, tv, iOS don't have a trash can
return false;
#endif
}
//static //static
bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source, bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
QFileSystemEntry &newLocation, QSystemError &error) QFileSystemEntry &newLocation, QSystemError &error)

View File

@ -127,6 +127,7 @@ public:
static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); static bool createLink(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); static bool copyFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool supportsMoveFileToTrash();
static bool moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error); static bool moveFileToTrash(const QFileSystemEntry &source, QFileSystemEntry &newLocation, QSystemError &error);
static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); static bool renameFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);
static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error); static bool renameOverwriteFile(const QFileSystemEntry &source, const QFileSystemEntry &target, QSystemError &error);

View File

@ -1184,6 +1184,12 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
// see qfilesystemengine_mac.mm // see qfilesystemengine_mac.mm
#elif defined(QT_BOOTSTRAPPED) || !defined(AT_FDCWD) #elif defined(QT_BOOTSTRAPPED) || !defined(AT_FDCWD)
// bootstrapped tools don't need this, and we don't want QStorageInfo // bootstrapped tools don't need this, and we don't want QStorageInfo
//static
bool QFileSystemEngine::supportsMoveFileToTrash()
{
return false;
}
//static //static
bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &, QFileSystemEntry &, bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &, QFileSystemEntry &,
QSystemError &error) QSystemError &error)
@ -1195,6 +1201,11 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &, QFileSystemEnt
/* /*
Implementing as per https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html Implementing as per https://specifications.freedesktop.org/trash-spec/trashspec-1.0.html
*/ */
//static
bool QFileSystemEngine::supportsMoveFileToTrash()
{
return true;
}
namespace { namespace {
struct FreeDesktopTrashOperation struct FreeDesktopTrashOperation

View File

@ -1772,6 +1772,12 @@ bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &
return ret; return ret;
} }
//static
bool QFileSystemEngine::supportsMoveFileToTrash()
{
return true;
}
/* /*
If possible, we use the IFileOperation implementation, which allows us to determine If possible, we use the IFileOperation implementation, which allows us to determine
the location of the object in the trash. the location of the object in the trash.

View File

@ -287,6 +287,7 @@ private slots:
void reuseQFile(); void reuseQFile();
void supportsMoveToTrash();
void moveToTrash_data(); void moveToTrash_data();
void moveToTrash(); void moveToTrash();
void moveToTrashDuplicateName(); void moveToTrashDuplicateName();
@ -3969,6 +3970,27 @@ void tst_QFile::reuseQFile()
} }
} }
void tst_QFile::supportsMoveToTrash()
{
// enforce the result according to our current implementation details
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
// Windows and macOS: definitely supported
QVERIFY(QFile::supportsMoveToTrash());
#elif defined(Q_OS_DARWIN)
// Other Darwin platforms: not supported
// (though Apple docs say trashItemAtURL is supported)
QVERIFY(!QFile::supportsMoveToTrash());
#elif defined(Q_OS_ANDROID)
// Android: not supported (we get EACCES even for $HOME files)
QVERIFY(!QFile::supportsMoveToTrash());
#elif !defined(AT_FDCWD)
// Unix platforms without the POSIX atfile support: not supported
QVERIFY(!QFile::supportsMoveToTrash());
#else
QVERIFY(QFile::supportsMoveToTrash());
#endif
}
void tst_QFile::moveToTrash_data() void tst_QFile::moveToTrash_data()
{ {
QTest::addColumn<QString>("source"); QTest::addColumn<QString>("source");
@ -4025,9 +4047,9 @@ void tst_QFile::moveToTrash_data()
void tst_QFile::moveToTrash() void tst_QFile::moveToTrash()
{ {
#if defined(Q_OS_ANDROID) or defined(Q_OS_WEBOS) or defined(Q_OS_VXWORKS) if (!QFile::supportsMoveToTrash())
QSKIP("This platform doesn't implement a trash bin"); QSKIP("This platform doesn't implement a trash bin");
#endif
QFETCH(QString, source); QFETCH(QString, source);
QFETCH(bool, create); QFETCH(bool, create);
QFETCH(bool, result); QFETCH(bool, result);
@ -4127,9 +4149,9 @@ void tst_QFile::moveToTrash()
void tst_QFile::moveToTrashDuplicateName() void tst_QFile::moveToTrashDuplicateName()
{ {
#if defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS) || defined(Q_OS_VXWORKS) if (!QFile::supportsMoveToTrash())
QSKIP("This platform doesn't implement a trash bin"); QSKIP("This platform doesn't implement a trash bin");
#endif
QString origFileName = []() { QString origFileName = []() {
QTemporaryFile temp(QDir::homePath() + "/tst_qfile.moveToTrashOpenFile.XXXXXX"); QTemporaryFile temp(QDir::homePath() + "/tst_qfile.moveToTrashOpenFile.XXXXXX");
temp.setAutoRemove(false); temp.setAutoRemove(false);
@ -4182,9 +4204,9 @@ void tst_QFile::moveToTrashOpenFile_data()
void tst_QFile::moveToTrashOpenFile() void tst_QFile::moveToTrashOpenFile()
{ {
#if defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS) || defined(Q_OS_VXWORKS) if (!QFile::supportsMoveToTrash())
QSKIP("This platform doesn't implement a trash bin"); QSKIP("This platform doesn't implement a trash bin");
#endif
QFETCH(bool, useStatic); QFETCH(bool, useStatic);
QFETCH(bool, success); QFETCH(bool, success);
const QByteArrayView contents = "Hello, World\n"; const QByteArrayView contents = "Hello, World\n";
@ -4242,9 +4264,9 @@ void tst_QFile::moveToTrashOpenFile()
void tst_QFile::moveToTrashSymlinkToFile() void tst_QFile::moveToTrashSymlinkToFile()
{ {
#if defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS) || defined(Q_OS_VXWORKS) if (!QFile::supportsMoveToTrash())
QSKIP("This platform doesn't implement a trash bin"); QSKIP("This platform doesn't implement a trash bin");
#endif
QTemporaryFile temp(QDir::homePath() + "/tst_qfile.moveToTrashSymlinkFile.XXXXXX"); QTemporaryFile temp(QDir::homePath() + "/tst_qfile.moveToTrashSymlinkFile.XXXXXX");
QVERIFY2(temp.open(), "Failed to create temporary file: " + temp.errorString().toLocal8Bit()); QVERIFY2(temp.open(), "Failed to create temporary file: " + temp.errorString().toLocal8Bit());
@ -4281,9 +4303,9 @@ void tst_QFile::moveToTrashSymlinkToDirectory_data()
void tst_QFile::moveToTrashSymlinkToDirectory() void tst_QFile::moveToTrashSymlinkToDirectory()
{ {
#if defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS) || defined(Q_OS_VXWORKS) if (!QFile::supportsMoveToTrash())
QSKIP("This platform doesn't implement a trash bin"); QSKIP("This platform doesn't implement a trash bin");
#endif
QFETCH(bool, appendSlash); QFETCH(bool, appendSlash);
QTemporaryDir temp(QDir::homePath() + "/tst_qfile.moveToTrashSymlinkDir.XXXXXX"); QTemporaryDir temp(QDir::homePath() + "/tst_qfile.moveToTrashSymlinkDir.XXXXXX");
QVERIFY2(temp.isValid(), "Failed to create temporary dir: " + temp.errorString().toLocal8Bit()); QVERIFY2(temp.isValid(), "Failed to create temporary dir: " + temp.errorString().toLocal8Bit());
@ -4315,9 +4337,9 @@ void tst_QFile::moveToTrashSymlinkToDirectory()
void tst_QFile::moveToTrashXdgSafety() void tst_QFile::moveToTrashXdgSafety()
{ {
#if defined(Q_OS_VXWORKS) if (!QFile::supportsMoveToTrash())
QSKIP("This platform doesn't implement a trash bin"); QSKIP("This platform doesn't implement a trash bin");
#endif
#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS) #if defined(Q_OS_WIN) || defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || defined(Q_OS_WEBOS)
QSKIP("This test is specific to XDG Unix systems"); QSKIP("This test is specific to XDG Unix systems");
#else #else