#include "TextMetrics.h"
#include "TexRow.h"
#include "TocBackend.h"
-#include "VSpace.h"
#include "WordLangTuple.h"
#include "insets/InsetBibtex.h"
+#include "insets/InsetCitation.h"
#include "insets/InsetCommand.h" // ChangeRefs
#include "insets/InsetExternal.h"
#include "insets/InsetGraphics.h"
#include "insets/InsetRef.h"
#include "insets/InsetText.h"
+#include "mathed/MathData.h"
+
#include "frontends/alert.h"
#include "frontends/Application.h"
#include "frontends/Delegates.h"
return 0;
}
+
/// Note that comparing contents can only be used for InsetCommand
bool findNextInset(DocIterator & dit, vector<InsetCode> const & codes,
docstring const & contents)
}
-docstring BufferView::contextMenu(int x, int y) const
+string BufferView::contextMenu(int x, int y) const
{
//If there is a selection, return the containing inset menu
if (d->cursor_.selection())
return Change(Change::UNCHANGED);
DocIterator dit = d->cursor_.selectionBegin();
+ // The selected content might have been changed (see #7685)
+ while (dit.inMathed())
+ // Find enclosing text cursor
+ dit.pop_back();
return dit.paragraph().lookupChange(dit.pos());
}
if (tm.contains(bot_pit)) {
ParagraphMetrics const & pm = tm.parMetrics(bot_pit);
- LASSERT(!pm.rows().empty(), /**/);
+ LBUFERR(!pm.rows().empty());
// FIXME: smooth scrolling doesn't work in mathed.
CursorSlice const & cs = dit.innerTextSlice();
int offset = coordOffset(dit).y_;
if (recenter)
scrolled = scroll(ypos - height_/2);
+ // We try to visualize the whole row, if the row height is larger than
+ // the screen height, we scroll to a heuristic value of height_ / 4.
+ // FIXME: This heuristic value should be replaced by a recursive search
+ // for a row in the inset that can be visualized completely.
+ else if (row_dim.height() > height_) {
+ if (ypos < defaultRowHeight())
+ scrolled = scroll(ypos - height_ / 4);
+ else if (ypos > height_ - defaultRowHeight())
+ scrolled = scroll(ypos - 3 * height_ / 4);
+ }
+
// If the top part of the row falls of the screen, we scroll
// up to align the top of the row with the top of the screen.
else if (ypos - row_dim.ascent() < 0 && ypos < height_) {
int ynew = row_dim.ascent();
- if (ynew > height_ - row_dim.descent())
- // FIXME: Recursively find the rowheight of the row in the inset
- // until we find a row which can be visualized completely.
- ynew = height_ - defaultRowHeight();
- int const scroll = ynew - ypos;
- scrolled = scrollUp(scroll);
+ scrolled = scrollUp(ynew - ypos);
}
// If the bottom of the row falls of the screen, we scroll down.
- // However, we have to be careful not to scroll that much that
- // the top falls of the screen.
else if (ypos + row_dim.descent() > height_ && ypos > 0) {
int ynew = height_ - row_dim.descent();
- if (ynew < row_dim.ascent())
- // FIXME: Recursively find the rowheight of the row in the inset
- // until we find a row which can be visualized completely.
- ynew = defaultRowHeight();
- int const scroll = ypos - ynew;
- scrolled = scrollDown(scroll);
+ scrolled = scrollDown(ypos - ynew);
}
// else, nothing to do, the cursor is already visible so we just return.
}
-void BufferView::updateDocumentClass(DocumentClass const * const olddc)
+void BufferView::makeDocumentClass()
+{
+ DocumentClassConstPtr olddc = buffer_.params().documentClassPtr();
+ buffer_.params().makeDocumentClass();
+ updateDocumentClass(olddc);
+}
+
+
+void BufferView::updateDocumentClass(DocumentClassConstPtr olddc)
{
message(_("Converting document to new document class..."));
buffer_.errors("Class Switch");
}
+
/** 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).
// buffer clean/dirty status by itself.
flag.setEnabled(!buffer_.isReadonly() && buffer_.undo().hasRedoStack());
break;
- case LFUN_FILE_INSERT:
case LFUN_FILE_INSERT_PLAINTEXT_PARA:
- case LFUN_FILE_INSERT_PLAINTEXT:
+ case LFUN_FILE_INSERT_PLAINTEXT: {
+ docstring const fname = cmd.argument();
+ if (!FileName::isAbsolute(to_utf8(fname))) {
+ flag.message(_("Absolute filename expected."));
+ return false;
+ }
+ flag.setEnabled(cur.inTexted());
+ break;
+ }
+ case LFUN_FILE_INSERT:
case LFUN_BOOKMARK_SAVE:
// FIXME: Actually, these LFUNS should be moved to Text
flag.setEnabled(cur.inTexted());
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:
case LFUN_MARK_ON:
case LFUN_BIBTEX_DATABASE_ADD:
case LFUN_BIBTEX_DATABASE_DEL:
case LFUN_STATISTICS:
- case LFUN_BRANCH_ADD_INSERT:
case LFUN_KEYMAP_OFF:
case LFUN_KEYMAP_PRIMARY:
case LFUN_KEYMAP_SECONDARY:
flag.setEnabled(true);
break;
+ case LFUN_WORD_FINDADV: {
+ FindAndReplaceOptions opt;
+ istringstream iss(to_utf8(cmd.argument()));
+ iss >> opt;
+ flag.setEnabled(opt.repl_buf_name.empty()
+ || !buffer_.isReadonly());
+ }
+
case LFUN_LABEL_GOTO: {
flag.setEnabled(!cmd.argument().empty()
|| getInsetByCode<InsetRef>(cur, REF_CODE));
case LFUN_CHANGES_TRACK:
flag.setEnabled(true);
- flag.setOnOff(buffer_.params().trackChanges);
+ flag.setOnOff(buffer_.params().track_changes);
break;
case LFUN_CHANGES_OUTPUT:
flag.setEnabled(true);
- flag.setOnOff(buffer_.params().outputChanges);
+ flag.setOnOff(buffer_.params().output_changes);
break;
case LFUN_CHANGES_MERGE:
flag.setEnabled(cur.inset().allowParagraphCustomization(cur.idx()));
break;
+ case LFUN_BRANCH_ADD_INSERT:
+ flag.setEnabled(!(cur.inTexted() && cur.paragraph().isPassThru()));
+ break;
+
case LFUN_DIALOG_SHOW_NEW_INSET:
// FIXME: this is wrong, but I do not understand the
// intent (JMarc)
void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
{
- //lyxerr << [ cmd = " << cmd << "]" << endl;
-
- // Make sure that the cached BufferView is correct.
- LYXERR(Debug::ACTION, " action[" << cmd.action() << ']'
- << " arg[" << to_utf8(cmd.argument()) << ']'
- << " x[" << cmd.x() << ']'
- << " y[" << cmd.y() << ']'
- << " button[" << cmd.button() << ']');
+ LYXERR(Debug::ACTION, "BufferView::dispatch: cmd: " << cmd);
string const argument = to_utf8(cmd.argument());
Cursor & cur = d->cursor_;
switch (act) {
case LFUN_BUFFER_PARAMS_APPLY: {
- DocumentClass const * const oldClass = buffer_.params().documentClassPtr();
+ DocumentClassConstPtr olddc = buffer_.params().documentClassPtr();
cur.recordUndoFullDocument();
istringstream ss(to_utf8(cmd.argument()));
Lexer lex;
<< unknown_tokens << " unknown token"
<< (unknown_tokens == 1 ? "" : "s"));
}
- updateDocumentClass(oldClass);
+ updateDocumentClass(olddc);
// 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
}
case LFUN_LAYOUT_MODULES_CLEAR: {
- DocumentClass const * const oldClass =
- buffer_.params().documentClassPtr();
cur.recordUndoFullDocument();
buffer_.params().clearLayoutModules();
- buffer_.params().makeDocumentClass();
- updateDocumentClass(oldClass);
+ makeDocumentClass();
dr.screenUpdate(Update::Force);
dr.forceBufferUpdate();
break;
case LFUN_LAYOUT_MODULE_ADD: {
BufferParams const & params = buffer_.params();
- if (!params.moduleCanBeAdded(argument)) {
+ if (!params.layoutModuleCanBeAdded(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();
- updateDocumentClass(oldClass);
+ makeDocumentClass();
dr.screenUpdate(Update::Force);
dr.forceBufferUpdate();
break;
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();
- updateDocumentClass(oldDocClass);
+ makeDocumentClass();
dr.screenUpdate(Update::Force);
dr.forceBufferUpdate();
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();
- updateDocumentClass(oldClass);
+ makeDocumentClass();
dr.screenUpdate(Update::Force);
dr.forceBufferUpdate();
break;
}
case LFUN_CHANGES_TRACK:
- buffer_.params().trackChanges = !buffer_.params().trackChanges;
+ buffer_.params().track_changes = !buffer_.params().track_changes;
break;
case LFUN_CHANGES_OUTPUT:
- buffer_.params().outputChanges = !buffer_.params().outputChanges;
- if (buffer_.params().outputChanges) {
+ buffer_.params().output_changes = !buffer_.params().output_changes;
+ if (buffer_.params().output_changes) {
bool dvipost = LaTeXFeatures::isAvailable("dvipost");
bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
LaTeXFeatures::isAvailable("xcolor");
case LFUN_WORD_FIND_FORWARD:
case LFUN_WORD_FIND_BACKWARD: {
+ // FIXME THREAD
+ // Would it maybe be better if this variable were view specific anyway?
static docstring last_search;
docstring searched_string;
docstring const data =
find2string(searched_string, true, false, fw);
bool found = lyxfind(this, FuncRequest(LFUN_WORD_FIND, data));
- if (found)
+ if (found) {
dr.screenUpdate(Update::Force | Update::FitCursor);
+ cur.dispatched();
+ dispatched = true;
+ } else {
+ cur.undispatched();
+ dispatched = false;
+ }
break;
}
lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "findreplace"));
break;
}
- if (lyxfind(this, req))
+ if (lyxfind(this, req)) {
dr.screenUpdate(Update::Force | Update::FitCursor);
- else
- message(_("String not found!"));
+ cur.dispatched();
+ dispatched = true;
+ } else {
+ cur.undispatched();
+ dispatched = false;
+ }
d->search_request_cache_ = req;
break;
}
if (lyxreplace(this, cmd, has_deleted)) {
dr.forceBufferUpdate();
dr.screenUpdate(Update::Force | Update::FitCursor);
+ cur.dispatched();
+ dispatched = true;
+ } else {
+ cur.undispatched();
+ dispatched = false;
}
break;
}
from = doc_iterator_begin(&buffer_);
to = doc_iterator_end(&buffer_);
}
- int const words = countWords(from, to);
- int const chars = countChars(from, to, false);
- int const chars_blanks = countChars(from, to, true);
+ buffer_.updateStatistics(from, to);
+ int const words = buffer_.wordCount();
+ int const chars = buffer_.charCount(false);
+ int const chars_blanks = buffer_.charCount(true);
docstring message;
if (cur.selection())
message = _("Statistics for the selection:");
p = Point(width_, height_);
Cursor old = cur;
bool const in_texted = cur.inTexted();
- cur.reset();
+ cur.setCursor(doc_iterator_begin(cur.buffer()));
+ cur.selHandle(false);
buffer_.changed(true);
updateHoveredInset();
d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_,
true, act == LFUN_SCREEN_UP);
- cur.resetAnchor();
//FIXME: what to do with cur.x_target()?
bool update = in_texted && cur.bv().checkDepm(cur, old);
cur.finishUndo();
- if (update) {
- dr.screenUpdate(Update::Force | Update::FitCursor);
+
+ if (update || cur.mark())
+ dr.screenUpdate(Update::Force | Update::FitCursor);
+ if (update)
dr.forceBufferUpdate();
- }
break;
}
- case LFUN_SCROLL:
- lfunScroll(cmd);
+ case LFUN_SCROLL: {
+ string const scroll_type = cmd.getArg(0);
+ int scroll_step = 0;
+ if (scroll_type == "line")
+ scroll_step = d->scrollbarParameters_.single_step;
+ else if (scroll_type == "page")
+ scroll_step = d->scrollbarParameters_.page_step;
+ else
+ return;
+ string const scroll_quantity = cmd.getArg(1);
+ if (scroll_quantity == "up")
+ scrollUp(scroll_step);
+ else if (scroll_quantity == "down")
+ scrollDown(scroll_step);
+ else {
+ int const scroll_value = convert<int>(scroll_quantity);
+ if (scroll_value)
+ scroll(scroll_step * scroll_value);
+ }
+ buffer_.changed(true);
+ updateHoveredInset();
dr.forceBufferUpdate();
break;
+ }
case LFUN_SCREEN_UP_SELECT: {
+ // FIXME: why is the algorithm different from LFUN_SCREEN_UP?
cur.selHandle(true);
if (isTopScreen()) {
lyx::dispatch(FuncRequest(LFUN_BUFFER_BEGIN_SELECT));
}
case LFUN_SCREEN_DOWN_SELECT: {
+ // FIXME: why is the algorithm different from LFUN_SCREEN_DOWN?
cur.selHandle(true);
if (isBottomScreen()) {
lyx::dispatch(FuncRequest(LFUN_BUFFER_END_SELECT));
FuncRequest const fr = lyxaction.lookupFunc(commandstr);
// an arbitrary number to limit number of iterations
- const int max_iter = 10000;
+ const int max_iter = 100000;
int iterations = 0;
Cursor & cur = d->cursor_;
Cursor const savecur = cur;
cur.forwardInset();
cur.beginUndoGroup();
while(cur && iterations < max_iter) {
- Inset * ins = cur.nextInset();
+ Inset * const ins = cur.nextInset();
if (!ins)
break;
docstring insname = ins->layoutName();
break;
insname = insname.substr(0, i);
}
- cur.forwardInset();
+ // if we did not delete the inset, skip it
+ if (!cur.nextInset() || cur.nextInset() == ins)
+ cur.forwardInset();
}
cur.endUndoGroup();
cur = savecur;
arg = token(argument, '|', 0);
opt1 = token(argument, '|', 1);
}
+
+ // if our cursor is directly in front of or behind a citation inset,
+ // we will instead add the new key to it.
+ Inset * inset = cur.nextInset();
+ if (!inset || inset->lyxCode() != CITE_CODE)
+ inset = cur.prevInset();
+ if (inset && inset->lyxCode() == CITE_CODE) {
+ InsetCitation * icite = static_cast<InsetCitation *>(inset);
+ if (icite->addKey(arg)) {
+ dr.forceBufferUpdate();
+ dr.screenUpdate(Update::FitCursor | Update::SinglePar);
+ if (!opt1.empty())
+ LYXERR0("Discarding optional argument to citation-insert.");
+ }
+ dispatched = true;
+ break;
+ }
InsetCommandParams icp(CITE_CODE);
icp["key"] = from_utf8(arg);
if (!opt1.empty())
Language const * newL = languages.getLanguage(argument);
if (!newL || oldL == newL)
break;
- if (oldL->rightToLeft() == newL->rightToLeft() && !buffer_.isMultiLingual()) {
+ if (oldL->rightToLeft() == newL->rightToLeft()) {
cur.recordUndoFullDocument();
buffer_.changeLanguage(oldL, newL);
cur.setCurrentFont();
break;
}
+ case LFUN_FILE_INSERT_PLAINTEXT_PARA:
+ case LFUN_FILE_INSERT_PLAINTEXT: {
+ bool const as_paragraph = (act == LFUN_FILE_INSERT_PLAINTEXT_PARA);
+ string const fname = to_utf8(cmd.argument());
+ if (!FileName::isAbsolute(fname))
+ dr.setMessage(_("Absolute filename expected."));
+ else
+ insertPlaintextFile(FileName(fname), as_paragraph);
+ break;
+ }
+
default:
// OK, so try the Buffer itself...
buffer_.dispatch(cmd, dr);
{
if (d->last_inset_ != inset) {
LYXERR0("Wrong last_inset!");
- LASSERT(false, /**/);
+ LATTEST(false);
}
d->last_inset_ = 0;
}
// Notify left insets
if (cur != old) {
- old.fixIfBroken();
- bool badcursor = notifyCursorLeavesOrEnters(old, cur);
+ bool badcursor = old.fixIfBroken() | cur.fixIfBroken();
+ badcursor |= notifyCursorLeavesOrEnters(old, cur);
if (badcursor)
cursor().fixIfBroken();
}
}
-void BufferView::lfunScroll(FuncRequest const & cmd)
-{
- string const scroll_type = cmd.getArg(0);
- int scroll_step = 0;
- if (scroll_type == "line")
- scroll_step = d->scrollbarParameters_.single_step;
- else if (scroll_type == "page")
- scroll_step = d->scrollbarParameters_.page_step;
- else
- return;
- string const scroll_quantity = cmd.getArg(1);
- if (scroll_quantity == "up")
- scrollUp(scroll_step);
- else if (scroll_quantity == "down")
- scrollDown(scroll_step);
- else {
- int const scroll_value = convert<int>(scroll_quantity);
- if (scroll_value)
- scroll(scroll_step * scroll_value);
- }
- buffer_.changed(true);
- updateHoveredInset();
-}
-
-
int BufferView::minVisiblePart()
{
return 2 * defaultRowHeight();
TextMetrics & BufferView::textMetrics(Text const * t)
{
- LASSERT(t, /**/);
+ LBUFERR(t);
TextMetricsCache::iterator tmc_it = d->text_metrics_.find(t);
if (tmc_it == d->text_metrics_.end()) {
tmc_it = d->text_metrics_.insert(
return false;
d->cursor_ = cur;
+
+ // we would rather not do this here, but it needs to be done before
+ // the changed() signal is sent.
+ buffer_.updateBuffer();
- cur.forceBufferUpdate();
buffer_.changed(true);
return true;
}
bool BufferView::mouseSetCursor(Cursor & cur, bool select)
{
- LASSERT(&cur.bv() == this, /**/);
+ LASSERT(&cur.bv() == this, return false);
if (!select)
// this event will clear selection so we save selection for
cap::saveSelection(cursor());
d->cursor_.macroModeClose();
+ // If a macro has been finalized, the cursor might have been broken
+ cur.fixIfBroken();
// Has the cursor just left the inset?
bool const leftinset = (&d->cursor_.inset() != &cur.inset());
bool BufferView::selectIfEmpty(DocIterator & cur)
{
- if (!cur.paragraph().empty())
+ if ((cur.inTexted() && !cur.paragraph().empty())
+ || (cur.inMathed() && !cur.cell().empty()))
return false;
pit_type const beg_pit = cur.pit();
void BufferView::insertLyXFile(FileName const & fname)
{
- LASSERT(d->cursor_.inTexted(), /**/);
+ LASSERT(d->cursor_.inTexted(), return);
// Get absolute path of file and add ".lyx"
// to the filename if necessary
CursorSlice const & sl = dit[0];
TextMetrics const & tm = textMetrics(sl.text());
ParagraphMetrics const & pm = tm.parMetrics(sl.pit());
- LASSERT(!pm.rows().empty(), /**/);
+
+ LBUFERR(!pm.rows().empty());
y -= pm.rows()[0].ascent();
#if 1
// FIXME: document this mess
}
-void BufferView::setInlineCompletion(Cursor & cur, DocIterator const & pos,
+void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos,
docstring const & completion, size_t uniqueChars)
{
uniqueChars = min(completion.size(), uniqueChars);