]> git.lyx.org Git - lyx.git/blobdiff - src/text3.C
Fix loop when opening TOC widget in an empty document, basically by Richard Heck.
[lyx.git] / src / text3.C
index 038354ea049d3222a27e7d0b14badf02c0072a17..8cb97ba60714f00fca1954526b2495fb90c34129 100644 (file)
@@ -76,8 +76,10 @@ namespace lyx {
 
 using cap::copySelection;
 using cap::cutSelection;
-using cap::pasteSelection;
+using cap::pasteFromStack;
+using cap::pasteClipboard;
 using cap::replaceSelection;
+using cap::saveSelection;
 
 using support::isStrUnsignedInt;
 using support::token;
@@ -121,8 +123,7 @@ namespace {
        {
                if (selecting || cur.mark())
                        cur.setSelection();
-               if (!cur.selection())
-                       theSelection().haveSelection(false);
+               saveSelection(cur);
                cur.bv().switchKeyMap();
        }
 
@@ -138,7 +139,6 @@ namespace {
        {
                recordUndo(cur);
                docstring sel = cur.selectionAsString(false);
-               //lyxerr << "selection is: '" << sel << "'" << endl;
 
                // It may happen that sel is empty but there is a selection
                replaceSelection(cur);
@@ -300,7 +300,7 @@ bool LyXText::isRTL(Buffer const & buffer, Paragraph const & par) const
 
 void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
 {
-       lyxerr[Debug::ACTION] << "LyXText::dispatch: cmd: " << cmd << endl;
+       LYXERR(Debug::ACTION) << "LyXText::dispatch: cmd: " << cmd << endl;
 
        // FIXME: We use the update flag to indicates wether a singlePar or a
        // full screen update is needed. We reset it here but shall we restore it
@@ -333,6 +333,10 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                std::swap(pars_[pit], pars_[pit + 1]);
 
                ParIterator begin(cur);
+               // begin.pos() (== cur.pos()) may point beyond the end of the 
+               // paragraph referenced by begin. This would cause a crash
+               // in updateLabels()
+               begin.pos() = 0;
                ++cur.pit();
                ParIterator end = boost::next(ParIterator(cur));
                updateLabels(cur.buffer(), begin, end);
@@ -347,7 +351,12 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                finishUndo();
                std::swap(pars_[pit], pars_[pit - 1]);
 
-               ParIterator end = boost::next(ParIterator(cur));
+               ParIterator end = ParIterator(cur);
+               // end.pos() (== cur.pos()) may point beyond the end of the 
+               // paragraph referenced by end. This would cause a crash
+               // in boost::next()
+               end.pos() = 0;
+               end = boost::next(end);
                --cur.pit();
                ParIterator begin(cur);
                updateLabels(cur.buffer(), begin, end);
@@ -434,6 +443,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_RIGHT);
                }
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_CHAR_BACKWARD:
@@ -450,6 +461,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_LEFT);
                }
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_UP:
@@ -464,6 +477,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_UP);
                }
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_DOWN:
@@ -478,18 +493,24 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_DOWN);
                }
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_PARAGRAPH_UP:
        case LFUN_PARAGRAPH_UP_SELECT:
                needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_UP_SELECT);
                needsUpdate |= cursorUpParagraph(cur);
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_PARAGRAPH_DOWN:
        case LFUN_PARAGRAPH_DOWN_SELECT:
                needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_DOWN_SELECT);
                needsUpdate |= cursorDownParagraph(cur);
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_SCREEN_UP:
@@ -501,6 +522,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                } else {
                        cursorPrevious(cur);
                }
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_SCREEN_DOWN:
@@ -513,6 +536,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                } else {
                        cursorNext(cur);
                }
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_LINE_BEGIN:
@@ -525,6 +550,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        case LFUN_LINE_END_SELECT:
                needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_END_SELECT);
                needsUpdate |= cursorEnd(cur);
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_WORD_FORWARD:
@@ -534,6 +561,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        needsUpdate |= cursorLeftOneWord(cur);
                else
                        needsUpdate |= cursorRightOneWord(cur);
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_WORD_BACKWARD:
@@ -543,6 +572,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        needsUpdate |= cursorRightOneWord(cur);
                else
                        needsUpdate |= cursorLeftOneWord(cur);
+               if (cur.selection())
+                       saveSelection(cur);
                break;
 
        case LFUN_WORD_SELECT: {
@@ -554,6 +585,10 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        case LFUN_BREAK_LINE: {
                // Not allowed by LaTeX (labels or empty par)
                if (cur.pos() > cur.paragraph().beginOfBody()) {
+                       // this avoids a double undo
+                       // FIXME: should not be needed, ideally
+                       if (!cur.selection())
+                               recordUndo(cur);
                        cap::replaceSelection(cur);
                        cur.insert(new InsetNewline);
                        cur.posRight();
@@ -695,6 +730,28 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                recordUndo(cur);
                InsetBase * inset = createInset(bv, cmd);
                if (inset) {
+                       // FIXME (Abdel 01/02/2006):
+                       // What follows would be a partial fix for bug 2154:
+                       //   http://bugzilla.lyx.org/show_bug.cgi?id=2154
+                       // This automatically put the label inset _after_ a 
+                       // numbered section. It should be possible to extend the mechanism
+                       // to any kind of LateX environement.
+                       // The correct way to fix that bug would be at LateX generation.
+                       // I'll let the code here for reference as it could be used for some
+                       // other feature like "automatic labelling".
+                       /*
+                       Paragraph & par = pars_[cur.pit()];
+                       if (inset->lyxCode() == InsetBase::LABEL_CODE
+                               && par.layout()->labeltype == LABEL_COUNTER) {
+                               // Go to the end of the paragraph
+                               // Warning: Because of Change-Tracking, the last
+                               // position is 'size()' and not 'size()-1':
+                               cur.pos() = par.size();
+                               // Insert a new paragraph
+                               FuncRequest fr(LFUN_BREAK_PARAGRAPH);
+                               dispatch(cur, fr);
+                       }
+                       */
                        insertInset(cur, inset);
                        cur.posRight();
                }
@@ -759,15 +816,15 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                cur.message(_("Paste"));
                cap::replaceSelection(cur);
                if (cmd.argument().empty() && !theClipboard().isInternal())
-                       pasteString(cur, theClipboard().get(), docstring());
+                       pasteClipboard(cur, bv->buffer()->errorList("Paste"));
                else {
                        string const arg(to_utf8(cmd.argument()));
-                       pasteSelection(cur, bv->buffer()->errorList("Paste"),
+                       pasteFromStack(cur, bv->buffer()->errorList("Paste"),
                                        isStrUnsignedInt(arg) ?
                                                convert<unsigned int>(arg) :
                                                0);
-                       bv->buffer()->errors("Paste");
                }
+               bv->buffer()->errors("Paste");
                cur.clearSelection(); // bug 393
                bv->switchKeyMap();
                finishUndo();
@@ -817,7 +874,7 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
 
        case LFUN_LAYOUT: {
                string layout = to_ascii(cmd.argument());
-               lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) " << layout << endl;
+               LYXERR(Debug::INFO) << "LFUN_LAYOUT: (arg) " << layout << endl;
 
                // Derive layout number from given argument (string)
                // and current buffer's textclass (number)
@@ -866,11 +923,15 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_CLIPBOARD_PASTE:
-               pasteString(cur, theClipboard().get(), cmd.argument());
+               cur.clearSelection();
+               pasteClipboard(cur, bv->buffer()->errorList("Paste"),
+                              cmd.argument() == "paragraph");
+               bv->buffer()->errors("Paste");
                break;
 
        case LFUN_PRIMARY_SELECTION_PASTE:
-               pasteString(cur, theSelection().get(), cmd.argument());
+               pasteString(cur, theSelection().get(),
+                           cmd.argument() == "paragraph");
                break;
 
        case LFUN_UNICODE_INSERT: {
@@ -889,22 +950,25 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        }
                
        case LFUN_QUOTE_INSERT: {
-               cap::replaceSelection(cur);
                Paragraph & par = cur.paragraph();
                pos_type pos = cur.pos();
-               char_type c;
-               if (pos == 0)
-                       c = ' ';
-               else if (cur.prevInset() && cur.prevInset()->isSpace())
-                       c = ' ';
-               else
-                       c = par.getChar(pos - 1);
-
-               LyXLayout_ptr const & style = par.layout();
-
                BufferParams const & bufparams = bv->buffer()->params();
+               LyXLayout_ptr const & style = par.layout();
                if (!style->pass_thru
                    && par.getFontSettings(bufparams, pos).language()->lang() != "hebrew") {
+                       // this avoids a double undo
+                       // FIXME: should not be needed, ideally
+                       if (!cur.selection())
+                               recordUndo(cur);
+                       cap::replaceSelection(cur);
+                       pos = cur.pos();
+                       char_type c;
+                       if (pos == 0)
+                               c = ' ';
+                       else if (cur.prevInset() && cur.prevInset()->isSpace())
+                               c = ' ';
+                       else
+                               c = par.getChar(pos - 1);
                        string arg = to_utf8(cmd.argument());
                        if (arg == "single")
                                cur.insert(new InsetQuotes(c,
@@ -937,7 +1001,7 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                        cursorEnd(cur);
                        cur.setSelection();
                        bv->cursor() = cur;
-                       theSelection().haveSelection(cur.selection());
+                       saveSelection(cur);
                }
                break;
 
@@ -945,7 +1009,6 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                if (cmd.button() == mouse_button::button1) {
                        selectWord(cur, WHOLE_WORD_STRICT);
                        bv->cursor() = cur;
-                       theSelection().haveSelection(cur.selection());
                }
                break;
 
@@ -960,15 +1023,11 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                // it could get cleared on the unlocking of the inset so
                // we have to check this first
                bool paste_internally = false;
-               if (cmd.button() == mouse_button::button2 && cur.selection()) {
-                       // Copy the selection to the clipboard stack. This
-                       // is done for two reasons:
-                       // - We want it to appear in the "Edit->Paste recent"
-                       //   menu.
-                       // - We can then use the normal copy/paste machinery
-                       //   instead of theSelection().get() to preserve
-                       //   formatting of the pasted stuff.
-                       cap::copySelectionToStack(cur.bv().cursor());
+               if (cmd.button() == mouse_button::button2 && cap::selection()) {
+                       // Copy the selection buffer to the clipboard
+                       // stack, because we want it to appear in the
+                       // "Edit->Paste recent" menu.
+                       cap::copySelectionToStack();
                        paste_internally = true;
                }
 
@@ -979,9 +1038,13 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                // if there is a local selection in the current buffer,
                // insert this
                if (cmd.button() == mouse_button::button2) {
-                       if (paste_internally)
-                               lyx::dispatch(FuncRequest(LFUN_PASTE, "0"));
-                       else
+                       if (paste_internally) {
+                               cap::pasteSelection(cur, bv->buffer()->errorList("Paste"));
+                               bv->buffer()->errors("Paste");
+                               cur.clearSelection(); // bug 393
+                               bv->switchKeyMap();
+                               finishUndo();
+                       } else
                                lyx::dispatch(FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph"));
                }
 
@@ -1038,10 +1101,18 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                if (cmd.button() == mouse_button::button2)
                        break;
 
-               // finish selection
                if (cmd.button() == mouse_button::button1) {
-                       if (cur.selection())
-                               theSelection().haveSelection(true);
+                       // if there is new selection, update persistent
+                       // selection, otherwise, single click does not
+                       // clear persistent selection buffer
+                       if (cur.selection()) {
+                               // finish selection
+                               // if double click, cur is moved to the end of word by selectWord
+                               // but bvcur is current mouse position
+                               LCursor & bvcur = cur.bv().cursor();
+                               bvcur.selection() = true;
+                               saveSelection(bvcur);
+                       }
                        needsUpdate = false;
                        cur.noUpdate();
                }
@@ -1060,11 +1131,9 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                // "auto_region_delete", which defaults to
                // true (on).
 
-               if (lyxrc.auto_region_delete) {
+               if (lyxrc.auto_region_delete)
                        if (cur.selection())
                                cutSelection(cur, false, false);
-                       theSelection().haveSelection(false);
-               }
 
                cur.clearSelection();
                LyXFont const old_font = real_current_font;
@@ -1072,12 +1141,8 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                docstring::const_iterator cit = cmd.argument().begin();
                docstring::const_iterator end = cmd.argument().end();
                for (; cit != end; ++cit)
-#if 0
                        bv->getIntl().getTransManager().
-                               translateAndInsert(*cit, this);
-#else
-                       insertChar(bv->cursor(), *cit);
-#endif
+                               translateAndInsert(*cit, this, cur);
 
                cur.resetAnchor();
                moveCursor(cur, false);
@@ -1119,8 +1184,14 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
 #if 0
        case LFUN_LIST_INSERT:
        case LFUN_THEOREM_INSERT:
-       case LFUN_CAPTION_INSERT:
 #endif
+       case LFUN_CAPTION_INSERT:
+               // Open the inset, and move the current selection
+               // inside it.
+               doInsertInset(cur, this, cmd, true, true);
+               cur.posRight();
+               updateLabels(*bv->buffer());
+               break;
        case LFUN_NOTE_INSERT:
        case LFUN_CHARSTYLE_INSERT:
        case LFUN_BOX_INSERT:
@@ -1148,20 +1219,47 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
 
        case LFUN_FLOAT_INSERT:
        case LFUN_FLOAT_WIDE_INSERT:
-       case LFUN_WRAP_INSERT:
+       case LFUN_WRAP_INSERT: {
+               bool content = cur.selection();  // will some text be moved into the inset?
+
                doInsertInset(cur, this, cmd, true, true);
                cur.posRight();
-               // FIXME: the "Caption" name should not be hardcoded,
-               // but given by the float definition.
-               cur.dispatch(FuncRequest(LFUN_LAYOUT, "Caption"));
+               ParagraphList & pars = cur.text()->paragraphs();
+
+               LyXTextClass const & tclass = bv->buffer()->params().getLyXTextClass();
+
+               // add a separate paragraph for the caption inset
+               pars.push_back(Paragraph());
+               pars.back().setInsetOwner(pars[0].inInset());
+               pars.back().layout(tclass.defaultLayout());
+
+               int cap_pit = pars.size() - 1;
+
+               // if an empty inset was created, we create an additional empty
+               // paragraph at the bottom so that the user can choose where to put
+               // the graphics (or table).
+               if (!content) {
+                       pars.push_back(Paragraph());
+                       pars.back().setInsetOwner(pars[0].inInset());
+                       pars.back().layout(tclass.defaultLayout());
+                       
+               }
+
+               // reposition the cursor to the caption
+               cur.pit() = cap_pit;
+               cur.pos() = 0;
+               cur.dispatch(FuncRequest(LFUN_CAPTION_INSERT));
+               // FIXME: When leaving the Float (or Wrap) inset we should
+               // delete any empty paragraph left above or below the
+               // caption.
                break;
+       }
 
        case LFUN_INDEX_INSERT:
        case LFUN_NOMENCL_INSERT: {
                InsetBase * inset = createInset(&cur.bv(), cmd);
                if (!inset)
                        break;
-
                recordUndo(cur);
                cur.clearSelection();
                insertInset(cur, inset);
@@ -1334,21 +1432,21 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_FINISHED_LEFT:
-               lyxerr[Debug::DEBUG] << "handle LFUN_FINISHED_LEFT:\n" << cur << endl;
+               LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_LEFT:\n" << cur << endl;
                break;
 
        case LFUN_FINISHED_RIGHT:
-               lyxerr[Debug::DEBUG] << "handle LFUN_FINISHED_RIGHT:\n" << cur << endl;
+               LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_RIGHT:\n" << cur << endl;
                ++cur.pos();
                break;
 
        case LFUN_FINISHED_UP:
-               lyxerr[Debug::DEBUG] << "handle LFUN_FINISHED_UP:\n" << cur << endl;
+               LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_UP:\n" << cur << endl;
                cursorUp(cur);
                break;
 
        case LFUN_FINISHED_DOWN:
-               lyxerr[Debug::DEBUG] << "handle LFUN_FINISHED_DOWN:\n" << cur << endl;
+               LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_DOWN:\n" << cur << endl;
                cursorDown(cur);
                break;
 
@@ -1421,12 +1519,12 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_CHANGE_ACCEPT: {
-               acceptChange(cur);
+               acceptOrRejectChanges(cur, ACCEPT);
                break;
        }
 
        case LFUN_CHANGE_REJECT: {
-               rejectChange(cur);
+               acceptOrRejectChanges(cur, REJECT);
                break;
        }
 
@@ -1466,6 +1564,7 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
        case LFUN_ESCAPE:
                if (cur.selection()) {
                        cur.selection() = false;
+                       saveSelection(cur);
                } else {
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_RIGHT);
@@ -1473,7 +1572,7 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
                break;
 
        default:
-               lyxerr[Debug::ACTION]
+               LYXERR(Debug::ACTION)
                        << BOOST_CURRENT_FUNCTION
                        << ": Command " << cmd
                        << " not DISPATCHED by LyXText" << endl;
@@ -1733,23 +1832,28 @@ bool LyXText::getStatus(LCursor & cur, FuncRequest const & cmd,
                break;
 
        case LFUN_PASTE:
-               // FIXME: This is not correct, but the correct code below is
-               // expensive
-               enable = cap::numberOfSelections() > 0 ||
-                       !theClipboard().isInternal();
-#if 0
                if (cmd.argument().empty()) {
                        if (theClipboard().isInternal())
                                enable = cap::numberOfSelections() > 0;
                        else
-                               enable = !theClipboard().get().empty();
-               } else if (isStrUnsignedInt(to_utf8(cmd.argument()))) {
-                       int n = convert<unsigned int>(to_utf8(cmd.argument()));
-                       enable = cap::numberOfSelections() > n;
-               } else
-                       // unknown argument
-                       enable = false;
-#endif
+                               enable = !theClipboard().empty();
+               } else {
+                       string const arg = to_utf8(cmd.argument());
+                       if (isStrUnsignedInt(arg)) {
+                               unsigned int n = convert<unsigned int>(arg);
+                               enable = cap::numberOfSelections() > n;
+                       } else
+                               // unknown argument
+                               enable = false;
+               }
+               break;
+
+       case LFUN_CLIPBOARD_PASTE:
+               enable = !theClipboard().empty();
+               break;
+
+       case LFUN_PRIMARY_SELECTION_PASTE:
+               enable = cur.selection() || !theSelection().empty();
                break;
 
        case LFUN_PARAGRAPH_MOVE_UP:
@@ -1822,8 +1926,6 @@ bool LyXText::getStatus(LCursor & cur, FuncRequest const & cmd,
        case LFUN_SERVER_GET_FONT:
        case LFUN_SERVER_GET_LAYOUT:
        case LFUN_LAYOUT:
-       case LFUN_CLIPBOARD_PASTE:
-       case LFUN_PRIMARY_SELECTION_PASTE:
        case LFUN_DATE_INSERT:
        case LFUN_SELF_INSERT:
        case LFUN_LINE_INSERT:
@@ -1832,6 +1934,7 @@ bool LyXText::getStatus(LCursor & cur, FuncRequest const & cmd,
        case LFUN_CLEARDOUBLEPAGE_INSERT:
        case LFUN_MATH_DISPLAY:
        case LFUN_MATH_IMPORT_SELECTION:
+       case LFUN_MATH_INSERT:
        case LFUN_MATH_MODE:
        case LFUN_MATH_MACRO:
        case LFUN_MATH_MATRIX:
@@ -1890,12 +1993,12 @@ bool LyXText::getStatus(LCursor & cur, FuncRequest const & cmd,
 
 
 void LyXText::pasteString(LCursor & cur, docstring const & clip,
-               docstring const & argument)
+               bool asParagraphs)
 {
        cur.clearSelection();
        if (!clip.empty()) {
                recordUndo(cur);
-               if (argument == "paragraph")
+               if (asParagraphs)
                        insertStringAsParagraphs(cur, clip);
                else
                        insertStringAsLines(cur, clip);