Monday, February 12, 2007

QTextEdit with drag-n-drop (cut-n-paste) of text images and links

I am right now waiting to get security clearance for a project and while that is taking place I have some extra time on my hands to indulge in Qt and some widget programming.

Step 1 in a 4-fun project was to make a simple Text Editor, preferably in Rich Text with embedded pictures or in second hand with html

Using QTextEdit and following a Qt example it was easy to get started. The example is not fully functional so some minor glitches had to be fixed but mostly it was just great seeing how the Trolls had done it and then redo what needed to be done differently to suit my needs.

Now one thing bugged me though. The QTextEdit comes already with drag-n-drop functionality (text, images etc) BUT it was not drag-cut-n-paste/drop that you would expect instead it was drag-copy-n-paste/drop. Do you understand what I mean here?

Anyhow. By subclassing QTextEdit it was possible to solve this - even though I racked my brains and had too much trial and error for my taste before it worked out. The Trolls HAD some information about how to do it
but not so much.

I.e. in short you need to implement two functions of your QTextEdit subclass. I call my subclass simply TextEdit
1.
virtual void dragEnterEvent(QDragEnterEvent *e);
In the implementation you simply put
void TextEdit::dragEnterEvent(QDragEnterEvent *e)
{
e->acceptProposedAction();
}
acceptProposedAction() simply agrees to make the drop action the (in Qt) defined for this QDragEnterEvent.

2. virtual void dropEvent(QDropEvent *e);
This tells your subclass exactly what to do when reciving a QDropEvent what we are interested in is to do a cut-n-paste of the selected area into the newly chosen (dragged) position.
In the implemantation (with lots of comments just for explaining :)

NOTE (Feb 18:) first I wrote this function as in the bottom of this entry, but ended up with inconsistent cursor behaviour so with the help of J-P at QtCentre.org I ended up with a clean and simple solution for this problem. The whole discussion you can find at : QCentre QTextEdit::dropEvent discussion

Working solution from J-P
void TextEdit::dropEvent(QDropEvent *event)
{
QDropEvent drop(event->pos(),Qt::MoveAction, event->mimeData(), event->mouseButtons(), Qt::ShiftModifier, event->type());
// Qt::ShiftModifier makes it into CUT instead of COPY

QTextEdit::dropEvent(&drop); // Call the parent function w/ the modified event

}


My original and non-working solution
void TextEdit::dropEvent(QDropEvent *event)
{
QTextCursor cursor = textCursor(); /*starting cursor */
event->acceptProposedAction(); /*Accept the incoming event*/

/*
Cursor for the selected position (drag-n-drop position)*/
QTextCursor insertionCursor = cursorForPosition(event->pos());


if (event->dropAction() == Qt::MoveAction
event->dropAction() == Qt::CopyAction)
{
/* Start atomic operation (undo/redo) */
insertionCursor.beginEditBlock();

/* Extract text before removing selected area */
QTextDocumentFragment text = cursor.selection();

/* Remove from original position */
cursor.removeSelectedText();

/* Insert dragged text/image/link */

insertionCursor.insertFragment( text );

/* End atomic operation (undo/redo) */
insertionCursor.endEditBlock();

/* The line below doesn't seem to work, Qt bug? */
setTextCursor( insertionCursor );

}
}




No comments: