Move files to Trash/Recycle Bin in Qt
Asked Answered
E

7

20

Is there a Qt function to move files to Recycle Bin instead of truly deleting them, for OSes that support it, or do I need to use OS-specific code?

Enfeoff answered 31/7, 2013 at 7:42 Comment(1)
Like @darthn said in an answer below QFile.moveToTrash has been added in Qt 5.15 (announcement). I hope this helps!Onanism
W
10

Since Qt 5.15.0 Alpha, this method has been added, which should be what you were looking for.

bool QFile::moveToTrash()

The corresponding code changes can be found here.

(This issue is old and the corresponding Bugreport at https://bugreports.qt.io/browse/QTBUG-47703 has already been posted, but I currently lack the reputation to comment, and found this to be a useful information.)

Weirdo answered 8/7, 2020 at 15:48 Comment(0)
S
8

Qt doesnt provide a MoveToTrash. Here's a part of my code

for Windows

#ifdef Q_OS_WIN32

#include "windows.h"

void MoveToTrashImpl( QString file ){
    QFileInfo fileinfo( file );
    if( !fileinfo.exists() )
        throw OdtCore::Exception( "File doesnt exists, cant move to trash" );
    WCHAR from[ MAX_PATH ];
    memset( from, 0, sizeof( from ));
    int l = fileinfo.absoluteFilePath().toWCharArray( from );
    Q_ASSERT( 0 <= l && l < MAX_PATH );
    from[ l ] = '\0';
    SHFILEOPSTRUCT fileop;
    memset( &fileop, 0, sizeof( fileop ) );
    fileop.wFunc = FO_DELETE;
    fileop.pFrom = from;
    fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    int rv = SHFileOperation( &fileop );
    if( 0 != rv ){
        qDebug() << rv << QString::number( rv ).toInt( 0, 8 );
        throw OdtCore::Exception( "move to trash failed" );
    }
}
#endif

and for Linux

#ifdef Q_OS_LINUX

bool TrashInitialized = false;
QString TrashPath;
QString TrashPathInfo;
QString TrashPathFiles;

void MoveToTrashImpl( QString file ){
    #ifdef QT_GUI_LIB
        if( !TrashInitialized ){
            QStringList paths;
            const char* xdg_data_home = getenv( "XDG_DATA_HOME" );
            if( xdg_data_home ){
                qDebug() << "XDG_DATA_HOME not yet tested";
                QString xdgTrash( xdg_data_home );
                paths.append( xdgTrash + "/Trash" );
            }
            QString home = QStandardPaths::writableLocation( QStandardPaths::HomeLocation );
            paths.append( home + "/.local/share/Trash" );
            paths.append( home + "/.trash" );
            foreach( QString path, paths ){
                if( TrashPath.isEmpty() ){
                    QDir dir( path );
                    if( dir.exists() ){
                        TrashPath = path;
                    }
                }
            }
            if( TrashPath.isEmpty() )
                throw Exception( "Cant detect trash folder" );
            TrashPathInfo = TrashPath + "/info";
            TrashPathFiles = TrashPath + "/files";
            if( !QDir( TrashPathInfo ).exists() || !QDir( TrashPathFiles ).exists() )
                throw Exception( "Trash doesnt looks like FreeDesktop.org Trash specification" );
            TrashInitialized = true;
        }
        QFileInfo original( file );
        if( !original.exists() )
            throw Exception( "File doesnt exists, cant move to trash" );
        QString info;
        info += "[Trash Info]\nPath=";
        info += original.absoluteFilePath();
        info += "\nDeletionDate=";
        info += QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss.zzzZ");
        info += "\n";
        QString trashname = original.fileName();
        QString infopath = TrashPathInfo + "/" + trashname + ".trashinfo";
        QString filepath = TrashPathFiles + "/" + trashname;
        int nr = 1;
        while( QFileInfo( infopath ).exists() || QFileInfo( filepath ).exists() ){
            nr++;
            trashname = original.baseName() + "." + QString::number( nr );
            if( !original.completeSuffix().isEmpty() ){
                trashname += QString( "." ) + original.completeSuffix();
            }
            infopath = TrashPathInfo + "/" + trashname + ".trashinfo";
            filepath = TrashPathFiles + "/" + trashname;
        }
        QDir dir;
        if( !dir.rename( original.absoluteFilePath(), filepath ) ){
            throw Exception( "move to trash failed" );
        }
        File infofile;
        infofile.createUtf8( infopath, info );
    #else
        Q_UNUSED( file );
        throw Exception( "Trash in server-mode not supported" );
    #endif
}
#endif
Solly answered 31/7, 2013 at 15:12 Comment(0)
E
5

There is no API yet.

https://bugreports.qt.io/browse/QTBUG-181

The issue is closed and the fix version is: Some future release

Edit: A new issue has been opened at https://bugreports.qt.io/browse/QTBUG-47703.

Edit Apparently it is now done in 5.15.0 Alpha bool QFile::moveToTrash()

Entrammel answered 31/7, 2013 at 8:43 Comment(3)
Weird issue handling. Closed as...? Won't fix? Too hard? Usually, when the answer is "maybe later", the issue is just left open.Kathikathiawar
There is a new issue that reopens this old one here: bugreports.qt.io/browse/QTBUG-47703Youngs
Update: Moving to trash should be implemented in Qt 5.15.Expire
L
3

I think that there is no cross-platform way. Simple moving files to "trash" location will not give effect, because user may switch off this possibility.

Maybe, this url will help: http://www.hardcoded.net/articles/send-files-to-trash-on-all-platforms.htm

Lyse answered 31/7, 2013 at 7:58 Comment(2)
The article is for Python however, not for C++.Enfeoff
@Enfeoff The article is about used API's. It doesn't matter from what language to call them.Lyse
K
3

I'm relatively certain that there is no Qt API that wraps this for all supported platforms. That means, unfortunately, that you will have to write platform-specific code.

I don't know anything about where/how Linux distributions store deleted files, and I imagine that it probably varies depending on which file manager you're using. I believe that moving files to a ~/.Trash folder is the standard way of doing it, but I'm not sure if this is reliable. For example, in the case of files stored on external volumes.

Things are a bit easier on Mac OS X, where there is a supported API to do this: FSMoveObjectToTrashSync, provided by Core Services. At least, that's how I remember you're supposed to do it. The documentation claims that this method is now deprecated in OS X 10.8. I have no idea what the recommended alternative is.

As a Windows programmer, I think that platform is much easier. :-) The basic solution is to call the SHFileOperation function:

#include <Windows.h>   // general Windows header file
#include <ShellAPI.h>  // for shell functions, like SHFileOperation
#include <string>      // (or use QString)

void RecycleFileOnWindows()
{
   std::wstring path = L"C:\\Users\\Administrator\\Documents\\deleteme.txt";
   path.append(1, L'\0');        // path string must be double nul-terminated

   SHFILEOPSTRUCT shfos = {};
   shfos.hwnd   = nullptr;       // handle to window that will own generated windows, if applicable
   shfos.wFunc  = FO_DELETE;
   shfos.pFrom  = path.c_str();
   shfos.pTo    = nullptr;       // not used for deletion operations
   shfos.fFlags = FOF_ALLOWUNDO; // use the recycle bin

   const int retVal = SHFileOperation(&shfos);
   if (retVal != 0)
   {
      // The operation failed...
      if (shfos.fAnyOperationsAborted)
      {
         // ...but that's because the user canceled.
         MessageBox(nullptr, L"Operation was canceled", nullptr, MB_OK | MB_ICONINFORMATION);
      }
      else
      {
         // ...for one of the other reasons given in the documentation.
         MessageBox(nullptr, L"Operation failed", nullptr, MB_OK | MB_ICONERROR);
      }
   }
}

There are also flags that you can set to customize confirmation, error reporting, and other behavior. The linked documentation contains all the details you need to build upon this basic example.

On Windows Vista and later, the SHFileOperation function has been superseded by the methods provided by the IFileOperation interface. If you're targeting only these later versions of Windows, you should prefer to use this interface. Otherwise, SHFileOperation will continue to work fine.

Kathikathiawar answered 31/7, 2013 at 8:5 Comment(2)
On Linux there is the FreeDesktop.org Trash specification which seems to talk about the case of files stored on external volumes. It is supported by many popular Linux file managers including Nautilus and Dolphin.Extine
Or the Qt guys could be pragmatic and just implement it for Ubuntu and the next 1-2 most popular distros.Enfeoff
E
1
if(QSysInfo::kernelType()=="linux")
{
    QDateTime currentTime(QDateTime::currentDateTime());    // save System time

    QString trashFilePath=QDir::homePath()+"/.local/share/Trash/files/";    // trash file path contain delete files
    QString trashInfoPath=QDir::homePath()+"/.local/share/Trash/info/";     // trash info path contain delete files information

    // create file format for trash info file----- START
    QFile infoFile(trashInfoPath+FileName.completeBaseName()+"."+FileName.completeSuffix()+".trashinfo");     //filename+extension+.trashinfo //  create file information file in /.local/share/Trash/info/ folder

    infoFile.open(QIODevice::ReadWrite);

    QTextStream stream(&infoFile);         // for write data on open file

    stream<<"[Trash Info]"<<endl;
    stream<<"Path="+QString(QUrl::toPercentEncoding(FileName.absoluteFilePath(),"~_-./"))<<endl;     // convert path string in percentage decoding scheme string
    stream<<"DeletionDate="+currentTime.toString("yyyy-MM-dd")+"T"+currentTime.toString("hh:mm:ss")<<endl;      // get date and time format YYYY-MM-DDThh:mm:ss

    infoFile.close();

    // create info file format of trash file----- END

    QDir file;
    file.rename(FileName.absoluteFilePath(),trashFilePath+FileName.completeBaseName()+"."+FileName.completeSuffix());  // rename(file old path, file trash path)



}
Ergo answered 5/8, 2016 at 4:51 Comment(2)
Welcome to stackoverflow. Please explain something about your answer.B
Just be careful with QSysInfo::kernelType()=="linux", Android's kernel is also Linux.Enfeoff
E
1

Trash files in linux exist /home/user_name/.local/share/Trash/files/ directory but it also require info file for each trash file which exist in /home/user_name/.local/share/Trash/info/ directory. when we want to move file into trash, actually move file into /home/user_name/.local/share/Trash/files/ directory and create info file in /home/user_name/.local/share/Trash/info/ directory. inside .trashinfo format use percentage decoding scheme for set file path where file existed, info file also contain time and date of deletion.

Ergo answered 21/8, 2016 at 13:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.