PDA

View Full Version : Drag-n-Drop with cut and not copy



KjellKod
17th February 2007, 17:10
Hi,

I would like to use QTextEdit with drag-n-drop but with CUT-n-PASTE and not the default COPY-n-PASTE.

To do this I I subclassed QTextEdit and and re-implemented
dragEnterEvent and
dropEvent(QDropEvent *event) . However,. even though it now works with cut-n-paste the cursor is all screwed up.

I.e. the cursor position is where I SELECTED the text and not where I dropped the text. IF I change that (setPosition, setTextCursor etc) I still end up with the problem that if I click somewhere on the document the 'ACTUAL' cursor moves to where I click BUT it is invisible, the cursor symbol is where the cursor was BEFORE I clicked on the document....

I COULD probably fix it by re-implementing more mouse events (mousePress, etc) but I think it should be possible to solve much easier.

So my questions are
1) Best way of getting cut-n-paste instead of copy-n-paste for drag-n-drop of text in a QTextEdit?

2) If it IS by subclassing dragEnterEvent and dropEvent then how can I fix the cursor problem?

See my code below for how it is implemented now


void TextEdit::dragEnterEvent(QDragEnterEvent *e)
{
e->acceptProposedAction();
}


void TextEdit::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();

if ( event->dropAction() == Qt::CopyAction)
{


QTextCursor cursor = textCursor(); // copy of cursor

// Drop position cursor
QTextCursor insertionCursor = cursorForPosition( event->pos() );

insertionCursor.beginEditBlock(); /* Start atomic operation (undo/redo) */

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

cursor.removeSelectedText(); /* Remove from original position */
insertionCursor.insertFragment( text ); /* Insert dragged text/image/link */

// Tried with set setTextCursor here but that only moved the visible cursor
// initially. Clicking on the document still gives an active invisible cursor
// AND a non-active visible cursor symbol

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

wysota
17th February 2007, 18:45
Try adding the following line at the end of your drop event:

setTextCursor(insertionCursor);

KjellKod
17th February 2007, 21:21
Try adding the following line at the end of your drop event:

setTextCursor(insertionCursor);

What happens then is that the active cursor gets to where the dropped text went (which is what I want), BUT if I click with the mouse on the text document the cursor symbol stays where it was while the invisible cursor (the active one) moves to where the mouse click went.
- is this fuzzy or am I explaining it OK?

What I want is of course them to be one and the same - but preferably w/out having to re-implement ALL mouseEvents

wysota
17th February 2007, 23:07
- is this fuzzy or am I explaining it OK?

I think J-P already stated in some other thread that he didn't understand it and I have to agree with him - I don't understand either...

Which cursor does QTextEdit::textCursor() return?

KjellKod
18th February 2007, 08:02
I think J-P already stated in some other thread that he didn't understand it and I have to agree with him - I don't understand either...

Which cursor does QTextEdit::textCursor() return?

Yeah. I know it's a little confusing - I think I read that thread too - as far as I could see other people had the same problem but I didn't find a solution for it.

QTextEdit::textCursor() returns a copy of the actual cursor. So any changes to that cursor will NOT affect the 'active' cursor.

I've attached a main.cpp file and my TextEdit I don't know if you have time to check it but if so the following steps would explain the problem
1) write some text - whatever to play with
2) select part of the text with the mouse - and do a drag-n-drop of that text to another section of the document
3) You will see that (correctly) the cursor moved to the place where the text was dropped.
4) Click on another location on the document - you will :( see that the curser symbol didn't move to the place where you clicked.
5) Try pressing some keys on the keyboard - the letters will appear where you latest clicked with the mouse and NOT where the visible cursor symbol is.

KjellKod
18th February 2007, 08:03
I noticed that I had made some minor changes to TextEdit in the zip file - compared to example above - it was for testing and doesn't affect the behaviour

jpn
18th February 2007, 10:32
Ok, the example speaks for itself. Here's a workaround:


void TextEdit::dropEvent(QDropEvent *event)
{
QDropEvent drop(event->pos(), Qt::MoveAction, event->mimeData(),
event->mouseButtons(), Qt::ShiftModifier, event->type()); // <-- Qt::ShiftModifier is important
QTextEdit::dropEvent(&drop);
}

KjellKod
18th February 2007, 11:11
That was a great workaround J-P ...
Simply by making a new dropEvent and calling the parent. I WILL use that one! :D
(Now why didn't I think of that :eek: )

But I am still confused how the QTextCursor got all messed up - do you know?

KjellKod
18th February 2007, 11:19
QDropEvent drop(event->pos(), Qt::MoveAction, event->mimeData(),
event->mouseButtons(), Qt::ShiftModifier, event->type()); // <-- Qt::ShiftModifier is important
}


Why is Qt::ShiftModifier important? Wouldn't it be better to use the actual modifiers for the event? I.e.

event->keyboardModifiers()
// instead of
Qt::ShiftModifier

jpn
18th February 2007, 11:23
But I am still confused how the QTextCursor got all messed up - do you know?
If you have enough interest, you can further investigate the issue by comparing the original code of yours and QTextControlPrivate::dropEvent() in src/gui/text/qtextcontrol.cpp.. :)

wysota
18th February 2007, 11:25
Wouldn't it be better to use the actual modifiers for the event? I.e.

event->keyboardModifiers()
// instead of
Qt::ShiftModifier


The whole point of the "hack" is to make Shift the modifier. This will change the event to move instead of copy. If you use keyboardModifiers(), you might "inherit" some other modifier which would make it a link instead of move.

jpn
18th February 2007, 11:30
Why is Qt::ShiftModifier important? Wouldn't it be better to use the actual modifiers for the event? I.e.

event->keyboardModifiers()
// instead of
Qt::ShiftModifier

It's important because the inner QTextControl ends up using QDropEvent::proposedAction(). The proposed action is determined by a private Qt class called QDragManager according to the modifiers (QDragManager::defaultAction() in src/gui/kernel/qdnd.cpp). I found all this out by simply debugging through the dropEvent() code.

KjellKod
18th February 2007, 11:38
I see what you mean - the shift modifier makes it into cut-n-paste rather than copy-n-paste

I've been a bit hesitant to look at Qt's code but I see now that when encountering problems like this that's definitely worthwhile :)

Thanks again J-P. :D

KjellKod
18th February 2007, 11:50
If you have enough interest, you can further investigate the issue by comparing the original code of yours and QTextControlPrivate::dropEvent() in src/gui/text/qtextcontrol.cpp.. :)

Now when I see the Trolls code I realize that I've tried to do exactly what they're doing. I belive that it is a bug in the Qt code since setting the cursor with
setTextCursor doesn't work.

jpn
18th February 2007, 11:57
I see what you mean - the shift modifier makes it into cut-n-paste rather than copy-n-paste
Yep, exactly. :)


I've been a bit hesitant to look at Qt's code but I see now that when encountering problems like this that's definitely worthwhile :)
Please don't hesitate to do so, it's definitely the most powerful way to find out why something works like it does.

The sources throughout Qt are pretty clean and understandable. However, one confusing thing when looking into the sources of Qt for the first time might be the usage of Pimpl (http://wiki.qtcentre.org/index.php?title=Private_Implementation). Whether it's a new concept or not, one gets quickly used to the way it's used in Qt.


Thanks again J-P. :D
No problem at all, happy hacking with Qt!