X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=cb06810f7aaafa248eab7cd0b858a32f0feb336e;hb=0a7705a6736a85c3c751f7b4e9998833cdb71fc9;hp=936dcffc9af6abe4442a3c424506bd7030a23eef;hpb=edb52c37d26b7872c73d2a4cec0b1b73c3b376ab;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 936dcffc9a..cb06810f7a 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -34,14 +34,16 @@ #include "InsetIterator.h" #include "Language.h" #include "LaTeXFeatures.h" +#include "LayoutFile.h" +#include "Lexer.h" #include "LyX.h" +#include "LyXAction.h" #include "lyxfind.h" #include "LyXFunc.h" #include "Layout.h" #include "LyXRC.h" #include "MetricsInfo.h" #include "Paragraph.h" -#include "paragraph_funcs.h" #include "ParagraphParameters.h" #include "ParIterator.h" #include "Session.h" @@ -83,6 +85,7 @@ #include #include #include +#include #include using namespace std; @@ -273,6 +276,9 @@ struct BufferView::Private /// Cache for Find Next FuncRequest search_request_cache_; + + /// + map edited_insets_; }; @@ -640,7 +646,7 @@ void BufferView::setCursorFromScrollbar() // We reset the cursor because cursorStatus() does not // work when the cursor is within mathed. Cursor cur(*this); - cur.reset(buffer_.inset()); + cur.reset(); tm.setCursorFromCoordinates(cur, 0, newy); // update the bufferview cursor and notify insets @@ -895,12 +901,87 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool recenter) } +void BufferView::updateLayout(DocumentClass const * const oldlayout) +{ + message(_("Converting document to new document class...")); + + StableDocIterator backcur(d->cursor_); + ErrorList & el = buffer_.errorList("Class Switch"); + cap::switchBetweenClasses( + oldlayout, buffer_.params().documentClassPtr(), + static_cast(buffer_.inset()), el); + + setCursor(backcur.asDocIterator(&buffer_)); + + buffer_.errors("Class Switch"); + buffer_.updateLabels(); +} + +/** Return the change status at cursor position, taking in account the + * status at each level of the document iterator (a table in a deleted + * footnote is deleted). + * When \param outer is true, the top slice is not looked at. + */ +static Change::Type lookupChangeType(DocIterator const & dit, bool outer = false) +{ + size_t const depth = dit.depth() - (outer ? 1 : 0); + + for (size_t i = 0 ; i < depth ; ++i) { + CursorSlice const & slice = dit[i]; + if (!slice.inset().inMathed() + && slice.pos() < slice.paragraph().size()) { + Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type; + if (ch != Change::UNCHANGED) + return ch; + } + } + return Change::UNCHANGED; +} + + bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) { + // Can we use a readonly buffer? + if (buffer_.isReadonly() + && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly) + && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) { + flag.message(from_utf8(N_("Document is read-only"))); + flag.setEnabled(false); + return true; + } + // Are we in a DELETED change-tracking region? + if (lookupChangeType(d->cursor_, true) == Change::DELETED + && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly) + && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) { + flag.message(from_utf8(N_("This portion of the document is deleted."))); + flag.setEnabled(false); + return true; + } + Cursor & cur = d->cursor_; + if (cur.getStatus(cmd, flag)) + return true; + switch (cmd.action) { + // FIXME: This is a bit problematic because we don't check is this is a + // document BufferView or not for these LFUNs. We probably have to + // dispatch both to currentBufferView() and, if that fails, + // to documentBufferView(); same as we do know for current Buffer and + // document Buffer. Ideally those LFUN should go to Buffer as they* + // operate on the full Buffer and the cursor is only needed either for + // an Undo record or to restore a cursor position. But we don't know + // how to do that inside Buffer of course. + case LFUN_BUFFER_PARAMS_APPLY: + case LFUN_LAYOUT_MODULES_CLEAR: + case LFUN_LAYOUT_MODULE_ADD: + case LFUN_LAYOUT_RELOAD: + case LFUN_TEXTCLASS_APPLY: + case LFUN_TEXTCLASS_LOAD: + flag.setEnabled(!buffer_.isReadonly()); + break; + case LFUN_UNDO: flag.setEnabled(buffer_.undo().hasUndoStack()); break; @@ -923,6 +1004,8 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_NOTE_NEXT: case LFUN_REFERENCE_NEXT: case LFUN_WORD_FIND: + case LFUN_WORD_FIND_FORWARD: + case LFUN_WORD_FIND_BACKWARD: case LFUN_WORD_FINDADV: case LFUN_WORD_REPLACE: case LFUN_MARK_OFF: @@ -936,12 +1019,16 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_ALL_INSETS_TOGGLE: case LFUN_STATISTICS: case LFUN_BRANCH_ADD_INSERT: + case LFUN_KEYMAP_OFF: + case LFUN_KEYMAP_PRIMARY: + case LFUN_KEYMAP_SECONDARY: + case LFUN_KEYMAP_TOGGLE: flag.setEnabled(true); break; - // @todo Test if current WorkArea is the search WorkArea case LFUN_REGEXP_MODE: - flag.setEnabled(! this->cursor().inRegexped()); + // FIXME: Test if current WorkArea is the search WorkArea + flag.setEnabled(buffer().isInternal() && !cur.inRegexped()); break; case LFUN_LABEL_COPY_AS_REF: { @@ -1019,11 +1106,38 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) break; case LFUN_DIALOG_SHOW_NEW_INSET: + // FIXME: this is wrong, but I do not understand the + // intent (JMarc) if (cur.inset().lyxCode() == CAPTION_CODE) return cur.inset().getStatus(cur, cmd, flag); - flag.setEnabled(cur.inset().lyxCode() != ERT_CODE && - cur.inset().lyxCode() != LISTINGS_CODE); + // FIXME we should consider passthru paragraphs too. + flag.setEnabled(!cur.inset().getLayout().isPassThru()); + break; + + case LFUN_CITATION_INSERT: { + FuncRequest fr(LFUN_INSET_INSERT, "citation"); + // FIXME: This could turn in a recursive hell. + // Shouldn't we use Buffer::getStatus() instead? + flag.setEnabled(lyx::getStatus(fr).enabled()); break; + } + case LFUN_INSET_APPLY: { + string const name = cmd.getArg(0); + Inset * inset = editedInset(name); + if (inset) { + FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument()); + FuncStatus fs; + if (!inset->getStatus(cur, fr, fs)) { + // Every inset is supposed to handle this + LASSERT(false, break); + } + flag |= fs; + } else { + FuncRequest fr(LFUN_INSET_INSERT, cmd.argument()); + flag |= lyx::getStatus(fr); + } + break; + } default: flag.setEnabled(false); @@ -1034,6 +1148,19 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) } +Inset * BufferView::editedInset(string const & name) const +{ + map::const_iterator it = d->edited_insets_.find(name); + return it == d->edited_insets_.end() ? 0 : it->second; +} + + +void BufferView::editInset(string const & name, Inset * inset) +{ + d->edited_insets_[name] = inset; +} + + bool BufferView::dispatch(FuncRequest const & cmd) { //lyxerr << [ cmd = " << cmd << "]" << endl; @@ -1045,10 +1172,108 @@ bool BufferView::dispatch(FuncRequest const & cmd) << " y[" << cmd.y << ']' << " button[" << cmd.button() << ']'); + string const argument = to_utf8(cmd.argument()); Cursor & cur = d->cursor_; + // Don't dispatch function that does not apply to internal buffers. + if (buffer_.isInternal() + && lyxaction.funcHasFlag(cmd.action, LyXAction::NoInternal)) + return false; + + // We'll set this back to false if need be. + bool dispatched = true; + buffer_.undo().beginUndoGroup(); + switch (cmd.action) { + case LFUN_BUFFER_PARAMS_APPLY: { + DocumentClass const * const oldClass = buffer_.params().documentClassPtr(); + cur.recordUndoFullDocument(); + istringstream ss(to_utf8(cmd.argument())); + Lexer lex; + lex.setStream(ss); + int const unknown_tokens = buffer_.readHeader(lex); + if (unknown_tokens != 0) { + LYXERR0("Warning in LFUN_BUFFER_PARAMS_APPLY!\n" + << unknown_tokens << " unknown token" + << (unknown_tokens == 1 ? "" : "s")); + } + updateLayout(oldClass); + + // We are most certainly here because of a change in the document + // It is then better to make sure that all dialogs are in sync with + // current document settings. + processUpdateFlags(Update::Force | Update::FitCursor); + break; + } + + case LFUN_LAYOUT_MODULES_CLEAR: { + DocumentClass const * const oldClass = + buffer_.params().documentClassPtr(); + cur.recordUndoFullDocument(); + buffer_.params().clearLayoutModules(); + buffer_.params().makeDocumentClass(); + updateLayout(oldClass); + processUpdateFlags(Update::Force | Update::FitCursor); + break; + } + + case LFUN_LAYOUT_MODULE_ADD: { + BufferParams const & params = buffer_.params(); + if (!params.moduleCanBeAdded(argument)) { + LYXERR0("Module `" << argument << + "' cannot be added due to failed requirements or " + "conflicts with installed modules."); + break; + } + DocumentClass const * const oldClass = params.documentClassPtr(); + cur.recordUndoFullDocument(); + buffer_.params().addLayoutModule(argument); + buffer_.params().makeDocumentClass(); + updateLayout(oldClass); + processUpdateFlags(Update::Force | Update::FitCursor); + break; + } + + case LFUN_TEXTCLASS_APPLY: { + if (!LayoutFileList::get().load(argument, buffer_.temppath()) && + !LayoutFileList::get().load(argument, buffer_.filePath())) + break; + + LayoutFile const * old_layout = buffer_.params().baseClass(); + LayoutFile const * new_layout = &(LayoutFileList::get()[argument]); + + if (old_layout == new_layout) + // nothing to do + break; + + //Save the old, possibly modular, layout for use in conversion. + DocumentClass const * const oldDocClass = + buffer_.params().documentClassPtr(); + cur.recordUndoFullDocument(); + buffer_.params().setBaseClass(argument); + buffer_.params().makeDocumentClass(); + updateLayout(oldDocClass); + processUpdateFlags(Update::Force | Update::FitCursor); + break; + } + + case LFUN_TEXTCLASS_LOAD: + LayoutFileList::get().load(argument, buffer_.temppath()) || + LayoutFileList::get().load(argument, buffer_.filePath()); + break; + + case LFUN_LAYOUT_RELOAD: { + DocumentClass const * const oldClass = buffer_.params().documentClassPtr(); + LayoutFileIndex bc = buffer_.params().baseClassID(); + LayoutFileList::get().reset(bc); + buffer_.params().setBaseClass(bc); + buffer_.params().makeDocumentClass(); + updateLayout(oldClass); + processUpdateFlags(Update::Force | Update::FitCursor); + break; + } + case LFUN_UNDO: cur.message(_("Undo")); cur.clearSelection(); @@ -1203,7 +1428,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) case LFUN_ALL_CHANGES_ACCEPT: // select complete document - cur.reset(buffer_.inset()); + cur.reset(); cur.selHandle(true); buffer_.text().cursorBottom(cur); // accept everything in a single step to support atomic undo @@ -1214,7 +1439,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) case LFUN_ALL_CHANGES_REJECT: // select complete document - cur.reset(buffer_.inset()); + cur.reset(); cur.selHandle(true); buffer_.text().cursorBottom(cur); // reject everything in a single step to support atomic undo @@ -1224,6 +1449,28 @@ bool BufferView::dispatch(FuncRequest const & cmd) processUpdateFlags(Update::Force | Update::FitCursor); break; + case LFUN_WORD_FIND_FORWARD: + case LFUN_WORD_FIND_BACKWARD: { + static docstring last_search; + docstring searched_string; + + if (!cmd.argument().empty()) { + last_search = cmd.argument(); + searched_string = cmd.argument(); + } else { + searched_string = last_search; + } + + if (searched_string.empty()) + break; + + bool const fw = cmd.action == LFUN_WORD_FIND_FORWARD; + docstring const data = + find2string(searched_string, true, false, fw); + find(this, FuncRequest(LFUN_WORD_FIND, data)); + break; + } + case LFUN_WORD_FIND: { FuncRequest req = cmd; if (cmd.argument().empty() && !d->search_request_cache_.argument().empty()) @@ -1419,7 +1666,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) p = Point(width_, height_); Cursor old = cur; bool const in_texted = cur.inTexted(); - cur.reset(buffer_.inset()); + cur.reset(); updateMetrics(); buffer_.changed(); d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_, @@ -1518,21 +1765,88 @@ bool BufferView::dispatch(FuncRequest const & cmd) Alert::warning(_("Branch already exists"), drtmp.message()); break; } - BranchList & branch_list = buffer_.params().branchlist(); - Branch const * branch = branch_list.find(branch_name); - string const x11hexname = X11hexname(branch->color()); - docstring const str = branch_name + ' ' + from_ascii(x11hexname); - lyx::dispatch(FuncRequest(LFUN_SET_COLOR, str)); lyx::dispatch(FuncRequest(LFUN_BRANCH_INSERT, branch_name)); break; } + case LFUN_KEYMAP_OFF: + getIntl().keyMapOn(false); + break; + + case LFUN_KEYMAP_PRIMARY: + getIntl().keyMapPrim(); + break; + + case LFUN_KEYMAP_SECONDARY: + getIntl().keyMapSec(); + break; + + case LFUN_KEYMAP_TOGGLE: + getIntl().toggleKeyMap(); + break; + + case LFUN_DIALOG_SHOW_NEW_INSET: { + string const name = cmd.getArg(0); + string data = trim(to_utf8(cmd.argument()).substr(name.size())); + if (decodeInsetParam(name, data, buffer_)) + lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data)); + else + lyxerr << "Inset type '" << name << + "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl; + break; + } + + case LFUN_CITATION_INSERT: { + if (argument.empty()) { + lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation")); + break; + } + // we can have one optional argument, delimited by '|' + // citation-insert | + // this should be enhanced to also support text_after + // and citation style + string arg = argument; + string opt1; + if (contains(argument, "|")) { + arg = token(argument, '|', 0); + opt1 = token(argument, '|', 1); + } + InsetCommandParams icp(CITE_CODE); + icp["key"] = from_utf8(arg); + if (!opt1.empty()) + icp["before"] = from_utf8(opt1); + string icstr = InsetCommand::params2string("citation", icp); + FuncRequest fr(LFUN_INSET_INSERT, icstr); + lyx::dispatch(fr); + break; + } + + case LFUN_INSET_APPLY: { + string const name = cmd.getArg(0); + Inset * inset = editedInset(name); + if (!inset) { + FuncRequest fr(LFUN_INSET_INSERT, cmd.argument()); + lyx::dispatch(fr); + break; + } + // put cursor in front of inset. + if (!setCursorFromInset(inset)) { + LASSERT(false, break); + } + cur.recordUndo(); + FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument()); + inset->dispatch(cur, fr); + processUpdateFlags(Update::SinglePar | Update::FitCursor); + break; + } default: - return false; + dispatched = false; + break; } - return true; + buffer_.undo().endUndoGroup(); + return dispatched; } @@ -1595,7 +1909,7 @@ Inset const * BufferView::getCoveringInset(Text const & text, if (!inset) return 0; - if (!inset->descendable()) + if (!inset->descendable(*this)) // No need to go further down if the inset is not // descendable. return inset; @@ -1802,11 +2116,12 @@ void BufferView::setCursorFromRow(int row) buffer_.texrow().getIdFromRow(row, tmpid, tmppos); - d->cursor_.reset(buffer_.inset()); + d->cursor_.reset(); if (tmpid == -1) buffer_.text().setCursor(d->cursor_, 0, 0); else buffer_.text().setCursor(d->cursor_, buffer_.getParFromID(tmpid).pit(), tmppos); + recenter(); } @@ -1818,7 +2133,7 @@ bool BufferView::setCursorFromInset(Inset const * inset) // Inset is not at cursor position. Find it in the document. Cursor cur(*this); - cur.reset(buffer().inset()); + cur.reset(); while (cur && cur.nextInset() != inset) cur.forwardInset(); @@ -1883,6 +2198,7 @@ int BufferView::workHeight() const void BufferView::setCursor(DocIterator const & dit) { + d->cursor_.reset(); size_t const n = dit.depth(); for (size_t i = 0; i < n; ++i) dit[i].inset().edit(d->cursor_, true); @@ -1927,6 +2243,8 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) // persistent selection cap::saveSelection(cursor()); + d->cursor_.macroModeClose(); + // Has the cursor just left the inset? bool leftinset = (&d->cursor_.inset() != &cur.inset()); if (leftinset) @@ -1942,9 +2260,9 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) bool update = leftinset; if (!do_selection && d->cursor_.inTexted()) update |= checkDepm(cur, d->cursor_); - d->cursor_.macroModeClose(); - d->cursor_.resetAnchor(); + if (!do_selection) + d->cursor_.resetAnchor(); d->cursor_.setCursor(cur); d->cursor_.boundary(cur.boundary()); if (do_selection) @@ -2337,8 +2655,12 @@ void BufferView::draw(frontend::Painter & pain) // and possibly grey out below pair lastpm = tm.last(); int const y2 = lastpm.second->position() + lastpm.second->descent(); - if (y2 < height_) - pain.fillRectangle(0, y2, width_, height_ - y2, Color_bottomarea); + + if (y2 < height_) { + Color color = buffer().isInternal() + ? Color_background : Color_bottomarea; + pain.fillRectangle(0, y2, width_, height_ - y2, color); + } break; } LYXERR(Debug::PAINTING, "\n\t\t*** END DRAWING ***"); @@ -2445,9 +2767,9 @@ void BufferView::insertPlaintextFile(FileName const & f, bool asParagraph) cap::replaceSelection(cur); buffer_.undo().recordUndo(cur); if (asParagraph) - cur.innerText()->insertStringAsParagraphs(cur, tmpstr); + cur.innerText()->insertStringAsParagraphs(cur, tmpstr, cur.current_font); else - cur.innerText()->insertStringAsLines(cur, tmpstr); + cur.innerText()->insertStringAsLines(cur, tmpstr, cur.current_font); updateMetrics(); buffer_.changed();