Search code examples
qtmime-typesclipboardqbytearray

How to implement clipboard actions for custom mime-types?


I am trying to implement copy/cut/paste in a complex application.
I have a QGraphicsScene that can contain QGraphicsItem subtypes of varied subtypes, fairly complex (with Item as a second parent storing custom properties).
I would copy/cut selected items, and paste them back in place.
I already have implemented it using a local version: a list of items.

void copyItemsActionOld()
{
    foreach(QGraphicsItem* qItem, selectedItems())
    {
        Item* newItem = (dynamic_cast<Item*>(qItem))->createItemCopy();
        m_itemClipboard.append(newItem);
    }
}

On paste, I make a copy of all items in clipboard and add them to the scene. So simple.....

BUT

I need to implement it using the global system clipboard.

I saw that creating a custom mime type is as simple as calling setData on a QMimeData object, after I make up a format name... (I hope that is true)

static const QString _mimeType("application/myItem");
void copyItemsAction()
{
    QMimeData* _mimeData = new QMimeData;
2    QByteArray _itemData = ?????;
    _mimeData->setData(_mimeType, _itemData);
    QClipboard* _clipboard = QApplication::clipboard();
    _clipboard->clear();
    _clipboard->setMimeData(_mimeData);
}

void pasteItemsAction()
{
    QClipboard* _clipboard = QApplication::clipboard();
    const QMimeData* _mimeData = _clipboard->mimeData();

    QStringList _formats = _mimeData->formats();
    foreach (QString _format, _formats)
    {
        if (_format == _mimeType)
        {
            QByteArray _itemData = _mimeData->data(_mimeType);
3           // then do what ? How do I parse it ?
        }
    }
}

My questions

1) Are the above fragments for copyItemsAction and pasteItemsAction anywhere close to how clipboard actions should work ?

2) How can I put item data in the QByteArray ?

3) How do I parse the data in QByteArray ?

4) Do I need to register the custom mime-type anywhere else ? (other than what I just did in my two functions); and will it be multi-platform ?

I have already implemented save and load functionality for all items. Something like...

void Item::saveItem(QDataStream &outFile)
{
    outFile << type;
    outFile << width;
    outFile << color.name();
}

Can I use this to place the items data in the QByteArray ? (How ?)


Solution

  • I was on the right track, and I kept adding code to my question until I found how to make it work:

    static const QString _mimeType("application/myItem");
    void copyItemsAction()
    {
        QByteArray _itemData;
        QDataStream outData(&_itemData, QIODevice::WriteOnly);
        outData << selectedItems().size();
        foreach(QGraphicsItem* qItem, selectedItems())
        {
            Item* item = dynamic_cast<Item*>(qItem);
            item->saveItem(outData);
        }
    
        QMimeData* _mimeData = new QMimeData;
        _mimeData->setData(_mimeType, _itemData);
        _mimeData->setText("My Items");
        QClipboard* _clipboard = QApplication::clipboard();
        _clipboard->clear();
        _clipboard->setMimeData(_mimeData);
    }
    
    void pasteItemsAction()
    {
        QClipboard* _clipboard = QApplication::clipboard();
        const QMimeData* _mimeData = _clipboard->mimeData();
    
        QStringList _formats = _mimeData->formats();
        foreach (QString _format, _formats)
        {
            if (_format == _mimeType)
            {
                QByteArray _itemData = _mimeData->data(_mimeType);
                QDataStream inData(&_itemData, QIODevice::ReadOnly);
                int itemsSize;
                inData >> itemsSize;
                for (int i = 0; i < itemsSize; ++i)
                {
                    Item* item = ...
                    item->loadItem(inData);
                }
            }
        }
    }
    

    So, for question 1, yes I was on the right track;

    For questions 2 and 3 - I was able to use a QDataStream to serialize info to/from the QByteArray.
    If there is a better / more effective / faster way, I would love to know...

    For question 4 - it seems that I can use just about any string, if all I want is to copy/paste within a single instance of my application.
    It is also true if I want to use it between multiple applications, multiple instances of my application, or for drag-and-drop - on most platforms. (It does not seem to work between multiple applications/instances in the embedded platform I target.)
    Caveat - it fails frequently when another clipboard using application is open, in windows.