Dragging out to non-Qt desktop



  • Hello,
    I’m working on dragging and dropping capability in my desktop app, which consists of multiple QWidget.
    D&D works OK if it’s internal movement where the drop receiver is another QWidget.
    My question is how can I achieve the same functionality between the desktop app and non Qt app such as Window desktop where no qt app exists.
    How and where can I handle the dropEvent outside of Qt?

    Many thanks in advance for your help.
    Sat


  • Moderators

    @ShinSat
    your question is very unclear.

    How and where can I handle the dropEvent outside of Qt?

    You shouldn't and you wont handle the drop outside your application. Similar to a copy and aste via clipboard you simply provide data to the operating system and let it do the multi-window drag-handling. The final target application which receives the drop handles the data on it's own.
    The only result you get is when your QDrag::exec() returnes and check the returned drop result.


  • Qt Champions 2016

    Hi
    To drop to desktop, you must follow windows rules for the mimedata
    else it wont accept.

    This sample drags file from Qt to desktop / explorer
    It uses a native windows header. (#include <Shlobj.h>)

    #include "mainwindow.h"
    #include <QApplication>
    #include <QtGui>
    #include <Shlobj.h>
    
    class Widget : public QWidget {
      void mousePressEvent(QMouseEvent* e) {
        QPoint hotSpot = e->pos();
        QList<QUrl> strList;
        FILEGROUPDESCRIPTOR fgd;
        FILEDESCRIPTOR fd[ 1 ];
    
        strList.append( QUrl( "http://www.qt.io/" ) );
        fgd.cItems = 1;
    
        fgd.fgd[ 0 ].dwFlags = FD_PROGRESSUI;
        wcscpy( fgd.fgd[ 0 ].cFileName, L"qtrules.url" );
        QMimeData* mimeData = new QMimeData;
        mimeData->setText("www.gaspar.net");
        mimeData->setUrls( strList );
        mimeData->setData("text/plain", QByteArray( "http://www.gaspar.net" ) );
        mimeData->setData("text/html", QByteArray( "<!--StartFragment--><a href=\"http://www.gaspar.net\">The Gaspar Website</a><!--EndFragment-->" ) );
        mimeData->setData("FileName", QByteArray( "Gaspar Website.url" ) );
        mimeData->setData("FileGroupDescriptorW", QByteArray::fromRawData( ( const char* )&fgd, sizeof ( FILEGROUPDESCRIPTOR ) ) );
        mimeData->setData("FileContents", QByteArray( "[InternetShortcut]\r\nURL=http://www.gaspar.net" ) );
    
        QPixmap pixmap(":/images/imagelists/16x16/webpage.png");
        render(&pixmap);
    
        QDrag* drag = new QDrag(this);
        drag->setMimeData(mimeData);
        drag->setPixmap(pixmap);
        drag->setHotSpot(hotSpot);
    
        Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
    
        if (dropAction == Qt::MoveAction) {
          update();
        }
      }
    };
    
    int main(int argc, char* argv[]) {
      QApplication app(argc, argv);
      Widget window;
      window.show();
      return app.exec();
    }
    

    Credits goes to http://www.gaspar.net/oteqmimewindows.html

    alt text



  • @mrjj
    There should be a "Code Snippets" forum here, and this kind of post could be put there with a decent title.


  • Qt Champions 2016

    @JNBarchan
    Yes, that would be great. I think we might be able to use the wiki for it since it already has samples.


  • Moderators

    @mrjj
    Just a note: File drags to desktop can also be achieved using Qt classes only:

    1. start a drag containing all the needed mime-types
    2. create an empty QTemporaryFile with the target file-name and set it's file path to the mime-data (file-urls type)
    3. fill the temporary file once it is requested in the QMimeData::retrieveData() method (see the delayed encoding example)
    4. the OS copies the (temp-)file to the drop-position

  • Qt Champions 2016

    @raven-worx

    Did you try delayed encoding sample ?

    The sample will not work for me on win 7 or win 10.
    Tried 2 pc. Desktop/explorer wont accept.

    So i wonder if issue with my pcs or just dont work like that in newer Windows.


  • Moderators

    @mrjj
    yes, but it's a while ago. I will give it a try.


  • Qt Champions 2016

    @raven-worx

    Super. Maybe i just do it wrong.
    I drag the export Button out from window. It show red stop sign.


  • Moderators

    @mrjj
    the following (unoptimized code) works (Qt 5.9, Windows 7):

    MimeData::MimeData()
        : QMimeData()
    {
        QString filePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) % QDir::separator() % "Gaspar Website.url";
        m_File = new QFile( filePath, this );
    }
    
    MimeData::~MimeData()
    {
        if( m_File && m_File->isOpen() )
            m_File->close();
    }
    
    QStringList MimeData::formats() const
    {
        return QMimeData::formats() << QLatin1String("text/uri-list");
    }
    
    QVariant MimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
    {
        if( mimeType == QStringLiteral("text/uri-list") )
        {
            // since this method may be called multiple during a single drag it's not necessary to also write to the file multiple times -> needs optimization
            if( m_File->open(QFile::WriteOnly) )
            {
                m_File->write( QByteArray("[InternetShortcut]\r\nURL=http://www.gaspar.net") );
                m_File->close();
            }
    
            return QVariant::fromValue<QUrl>( QUrl::fromLocalFile(QFileInfo(*m_File).absoluteFilePath()) );
        }
        else
            return QMimeData::retrieveData(mimeType, type);
    }
    

    Although it seems there are some caveats:

    1. it stopped working when i set the path of the temp file into a subfolder in the temp-dir ... o.O
    2. the retrieveData() always requests the data with the type QVariant::QVariantList. But still it only works when i return a QUrl (not a QList) packed into the variant data.

    Very strange.


  • Qt Champions 2016

    @raven-worx

    Good work.
    Ehh so it likes only root of tmp dir ?? odd
    2: that is a bit odd but i guess it was only ment for 1 file then.

    Would it be possible to get the test code ?
    Want to see if win 10 likes it also :)

    This is nicer than the platform bound i found :) \o/


  • Moderators

    @mrjj said in Dragging out to non-Qt desktop:

    Would it be possible to get the test code ?

    thats actually all the code (despite the obvious header file).

    All i did additionally for testing was this:

    void MainWindow::mousePressEvent(QMouseEvent *event)
    {
        event->accept();
    
        MimeData* mimeData = new MimeData;
    
        QDrag drag( this );
            drag.setMimeData(mimeData);
        drag.exec(Qt::CopyAction);
    }
    

    the mime type is text/uri-list, so a list does make sense. It also makes sense to copy multiple files at once to a folder in the filesystem explorer.
    I have no idea why it's not working. Would need to debug into Qt source code to make sure.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.