Analysis: Previously, any selection action triggers saveSelection, which saves selection and calls theSelection().haveSelection(cur.selection());. Therefore, when selecting with page down, saveSelection will be called many times, with larger and larger selected texts saving to the selection buffer. This is of course slow.
Solution: Instead of saveSelection when a selection is formed, this patch saves a selection when it is cleared. This sounds strange so let me explain: select, left button click, middle button click previously triggers select -> saveSelection, left button click -> clearSelection, middle button click -> paste and now select -> nothing, left button click -> saveSelection, clearSelection, middle button click -> paste In the page down selection case, the entire selection will only be copied to the selection buffer when the selection is pasted, or cleared by left/right/up/down/home/end/buffer switch etc. Therefore, bug 3877 is fixed. Problem: It is more difficult to catch 'selection clear' events than 'selection creation' events. I have caught all keyboard up/down/home/end etc, and BUFFER_SWITCH, but there is no WINDOW_SWITCH signal. That is to say, I can not select in one window and paste in another one. Note that selection in lyx, and paste outside of lyx is not affected. Abdel: how to catch window enter/leave/switch events? Patch: Attached patch is long but easy to understand: case LFUN_BUFFER_SWITCH: BOOST_ASSERT(lyx_view_); + // save current selection to the selection buffer to allow + // middle-button paste in another buffer + cap::saveSelection(view()->cursor()); lyx_view_->setBuffer(theBufferList().getBuffer(argument)); break; saveSelection before buffer switch, MANY cases like this: case LFUN_CHAR_FORWARD: + // save selection because char forward will clear selection + saveSelection(cur); case LFUN_CHAR_FORWARD_SELECT: //lyxerr << BOOST_CURRENT_FUNCTION // << " LFUN_CHAR_FORWARD[SEL]:\n" << cur << endl; @@ -488,11 +494,13 @@ Save selection with char-forward, backward, up, down, home, end etc. @@ -575,44 +588,48 @@ else { cursorNext(cur); } - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; Remove saveSelection after selection is formed. void saveSelection(Cursor & cur) { - LYXERR(Debug::ACTION) << BOOST_CURRENT_FUNCTION << ": `" - << to_utf8(cur.selectionAsString(true)) << "'." - << endl; + // This function is called, not when a selection is formed, but when + // a selection is cleared. Therefore, multiple keyboard selection + // will not repeatively trigger this function (bug 3877). + // The problem with this approach is that it is difficult to catch + // all cases when this function should be called so a few minor bugs + // are expected. + if (cur.selection()) { + LYXERR(Debug::ACTION) << BOOST_CURRENT_FUNCTION << ": `" + << to_utf8(cur.selectionAsString(true)) << "'." + << endl; - // FIXME: The two lines below would allow middle-mouse - // pasting that preserves the LyX formatting when the selection - // is internal. They would also allow to use the feature on - // Windows and Mac. In the future, we may want to optionally enable - // this feature via a rc setting. - /* - if (cur.selection()) copySelectionToStack(cur, selectionBuffer); - */ - - // tell X whether we now have a valid selection - theSelection().haveSelection(cur.selection()); + } } The saveSelection function does not do theSelection()... now. Conclusion: This is the best patch I can think of for BUG3877. Please test and I will commit with two OKs. Cheers, Bo
Index: src/LyXFunc.cpp =================================================================== --- src/LyXFunc.cpp (revision 18980) +++ src/LyXFunc.cpp (working copy) @@ -1184,6 +1184,9 @@ // --- buffers ---------------------------------------- case LFUN_BUFFER_SWITCH: BOOST_ASSERT(lyx_view_); + // save current selection to the selection buffer to allow + // middle-button paste in another buffer + cap::saveSelection(view()->cursor()); lyx_view_->setBuffer(theBufferList().getBuffer(argument)); break; Index: src/CutAndPaste.cpp =================================================================== --- src/CutAndPaste.cpp (revision 18980) +++ src/CutAndPaste.cpp (working copy) @@ -672,22 +672,19 @@ void saveSelection(Cursor & cur) { - LYXERR(Debug::ACTION) << BOOST_CURRENT_FUNCTION << ": `" - << to_utf8(cur.selectionAsString(true)) << "'." - << endl; + // This function is called, not when a selection is formed, but when + // a selection is cleared. Therefore, multiple keyboard selection + // will not repeatively trigger this function (bug 3877). + // The problem with this approach is that it is difficult to catch + // all cases when this function should be called so a few minor bugs + // are expected. + if (cur.selection()) { + LYXERR(Debug::ACTION) << BOOST_CURRENT_FUNCTION << ": `" + << to_utf8(cur.selectionAsString(true)) << "'." + << endl; - // FIXME: The two lines below would allow middle-mouse - // pasting that preserves the LyX formatting when the selection - // is internal. They would also allow to use the feature on - // Windows and Mac. In the future, we may want to optionally enable - // this feature via a rc setting. - /* - if (cur.selection()) copySelectionToStack(cur, selectionBuffer); - */ - - // tell X whether we now have a valid selection - theSelection().haveSelection(cur.selection()); + } } Index: src/Text3.cpp =================================================================== --- src/Text3.cpp (revision 18980) +++ src/Text3.cpp (working copy) @@ -119,7 +119,7 @@ { if (selecting || cur.mark()) cur.setSelection(); - saveSelection(cur); + theSelection().haveSelection(cur.selection()); } @@ -455,6 +455,8 @@ break; case LFUN_BUFFER_BEGIN: + // save selection because buffer begin will clear selection + saveSelection(cur); case LFUN_BUFFER_BEGIN_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_BEGIN_SELECT); if (cur.depth() == 1) { @@ -465,6 +467,8 @@ break; case LFUN_BUFFER_END: + // save selection because buffer end will clear selection + saveSelection(cur); case LFUN_BUFFER_END_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_END_SELECT); if (cur.depth() == 1) { @@ -475,6 +479,8 @@ break; case LFUN_CHAR_FORWARD: + // save selection because char forward will clear selection + saveSelection(cur); case LFUN_CHAR_FORWARD_SELECT: //lyxerr << BOOST_CURRENT_FUNCTION // << " LFUN_CHAR_FORWARD[SEL]:\n" << cur << endl; @@ -488,11 +494,13 @@ && cur.boundary() == oldBoundary) { cur.undispatched(); cmd = FuncRequest(LFUN_FINISHED_RIGHT); - } else if (cur.selection()) - saveSelection(cur); + } + theSelection().haveSelection(cur.selection()); break; case LFUN_CHAR_BACKWARD: + // save selection because char backward will clear selection + saveSelection(cur); case LFUN_CHAR_BACKWARD_SELECT: //lyxerr << "handle LFUN_CHAR_BACKWARD[_SELECT]:\n" << cur << endl; needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT); @@ -506,14 +514,16 @@ cur.undispatched(); cmd = FuncRequest(LFUN_FINISHED_LEFT); } - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; + case LFUN_UP: + case LFUN_DOWN: + // save selection because cursor up/down will clear selection + saveSelection(cur); case LFUN_UP_SELECT: case LFUN_DOWN_SELECT: - case LFUN_UP: - case LFUN_DOWN: { + { // stop/start the selection bool select = cmd.action == LFUN_DOWN_SELECT || cmd.action == LFUN_UP_SELECT; @@ -532,29 +542,31 @@ } else cur.undispatched(); - // save new selection - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; } case LFUN_PARAGRAPH_UP: + // save selection because paragraph up will clear selection + saveSelection(cur); case LFUN_PARAGRAPH_UP_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_UP_SELECT); needsUpdate |= cursorUpParagraph(cur); - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_PARAGRAPH_DOWN: + // save selection because paragraph down will clear selection + saveSelection(cur); case LFUN_PARAGRAPH_DOWN_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_DOWN_SELECT); needsUpdate |= cursorDownParagraph(cur); - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_SCREEN_UP: + // save selection because screen up will clear selection + saveSelection(cur); case LFUN_SCREEN_UP_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_SCREEN_UP_SELECT); if (cur.pit() == 0 && cur.textRow().pos() == 0) @@ -562,11 +574,12 @@ else { cursorPrevious(cur); } - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_SCREEN_DOWN: + // save selection because screen down will clear selection + saveSelection(cur); case LFUN_SCREEN_DOWN_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_SCREEN_DOWN_SELECT); if (cur.pit() == cur.lastpit() @@ -575,44 +588,48 @@ else { cursorNext(cur); } - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_LINE_BEGIN: + // save selection because line begin will clear selection + saveSelection(cur); case LFUN_LINE_BEGIN_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_BEGIN_SELECT); needsUpdate |= cursorHome(cur); break; case LFUN_LINE_END: + // save selection because line end will clear selection + saveSelection(cur); case LFUN_LINE_END_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_END_SELECT); needsUpdate |= cursorEnd(cur); - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_WORD_FORWARD: + // save selection because word forward will clear selection + saveSelection(cur); case LFUN_WORD_FORWARD_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT); if (reverseDirectionNeeded(cur)) needsUpdate |= cursorLeftOneWord(cur); else needsUpdate |= cursorRightOneWord(cur); - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_WORD_BACKWARD: + // save selection because word backward will clear selection + saveSelection(cur); case LFUN_WORD_BACKWARD_SELECT: needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT); if (reverseDirectionNeeded(cur)) needsUpdate |= cursorRightOneWord(cur); else needsUpdate |= cursorLeftOneWord(cur); - if (cur.selection()) - saveSelection(cur); + theSelection().haveSelection(cur.selection()); break; case LFUN_WORD_SELECT: { @@ -1033,7 +1050,7 @@ cursorEnd(cur); cur.setSelection(); bv->cursor() = cur; - saveSelection(cur); + theSelection().haveSelection(cur.selection()); } break; @@ -1046,6 +1063,9 @@ // Single-click on work area case LFUN_MOUSE_PRESS: { + // previous selection is going to be cleared, so we + // save it to selection buffer (for persistent selection) + saveSelection(cur.bv().cursor()); // Right click on a footnote flag opens float menu if (cmd.button() == mouse_button::button3) cur.clearSelection(); @@ -1145,7 +1165,7 @@ // but bvcur is current mouse position Cursor & bvcur = cur.bv().cursor(); bvcur.selection() = true; - saveSelection(bvcur); + theSelection().haveSelection(true); } needsUpdate = false; cur.noUpdate(); @@ -1589,7 +1609,7 @@ case LFUN_ESCAPE: if (cur.selection()) { cur.selection() = false; - saveSelection(cur); + theSelection().haveSelection(cur.selection()); } else { cur.undispatched(); cmd = FuncRequest(LFUN_FINISHED_RIGHT);