*
* \author Asger Alstrup
* \author Lars Gullik Bjønnes
+ * \author Richard Heck (XHTML output)
* \author Jean-Marc Lasgouttes
* \author Angus Leeming
* \author John Levon
#include "OutputParams.h"
#include "output_latex.h"
#include "output_xhtml.h"
-#include "paragraph_funcs.h"
#include "ParagraphParameters.h"
#include "SpellChecker.h"
#include "sgml.h"
OutputParams const & runparams);
///
- void validate(LaTeXFeatures & features,
- Layout const & layout) const;
+ void validate(LaTeXFeatures & features) const;
/// Checks if the paragraph contains only text and no inset or font change.
bool onlyText(Buffer const & buf, Font const & outerfont,
/// match a string against a particular point in the paragraph
bool isTextAt(string const & str, pos_type pos) const;
+
+
+ InsetCode ownerCode() const
+ {
+ return inset_owner_ ? inset_owner_->lyxCode() : NO_CODE;
+ }
/// Which Paragraph owns us?
Paragraph * owner_;
FontList fontlist_;
///
- unsigned int id_;
- ///
- static unsigned int paragraph_id;
+ int id_;
+
///
ParagraphParameters params_;
///
TextContainer text_;
- typedef std::set<docstring> Words;
+ typedef set<docstring> Words;
+ typedef map<Language, Words> LangWordsMap;
///
- Words words_;
+ LangWordsMap words_;
///
Layout const * layout_;
};
-// Initialization of the counter for the paragraph id's,
-unsigned int Paragraph::Private::paragraph_id = 0;
-
namespace {
struct special_phrase {
Paragraph::Private::Private(Paragraph * owner, Layout const & layout)
- : owner_(owner), inset_owner_(0), begin_of_body_(0), layout_(&layout)
+ : owner_(owner), inset_owner_(0), id_(-1), begin_of_body_(0), layout_(&layout)
{
- id_ = paragraph_id++;
text_.reserve(100);
}
+// Initialization of the counter for the paragraph id's,
+//
+// FIXME: There should be a more intelligent way to generate and use the
+// paragraph ids per buffer instead a global static counter for all InsetText
+// in the running program.
+static int paragraph_id = -1;
+
Paragraph::Private::Private(Private const & p, Paragraph * owner)
: owner_(owner), inset_owner_(p.inset_owner_), fontlist_(p.fontlist_),
params_(p.params_), changes_(p.changes_), insetlist_(p.insetlist_),
begin_of_body_(p.begin_of_body_), text_(p.text_), words_(p.words_),
layout_(p.layout_)
{
- id_ = paragraph_id++;
+ id_ = ++paragraph_id;
}
begin_of_body_(p.begin_of_body_), words_(p.words_),
layout_(p.layout_)
{
- id_ = paragraph_id++;
+ id_ = ++paragraph_id;
if (beg >= pos_type(p.text_.size()))
return;
text_ = p.text_.substr(beg, end - beg);
}
-bool Paragraph::isFullyDeleted(pos_type start, pos_type end) const
+bool Paragraph::isDeleted(pos_type start, pos_type end) const
{
LASSERT(start >= 0 && start <= size(), /**/);
LASSERT(end > start && end <= size() + 1, /**/);
- return d->changes_.isFullyDeleted(start, end);
+ return d->changes_.isDeleted(start, end);
}
return true;
Change const change = d->changes_.lookup(size());
- return change.type == Change::INSERTED && change.author == 0;
+ return change.inserted() && change.currentAuthor();
}
* Conclusion: An inset's content should remain untouched if you delete it
*/
- if (change.type != Change::DELETED) {
+ if (!change.deleted()) {
for (pos_type pos = 0; pos < size(); ++pos) {
if (Inset * inset = getInset(pos))
inset->setChange(change);
d->changes_.set(change, pos);
// see comment in setChange(Change const &) above
- if (change.type != Change::DELETED && pos < size())
+ if (!change.deleted() && pos < size())
if (Inset * inset = getInset(pos))
inset->setChange(change);
}
// a) it was previously unchanged or
// b) it was inserted by a co-author
- if (change.type == Change::UNCHANGED ||
- (change.type == Change::INSERTED && change.author != 0)) {
+ if (!change.changed() ||
+ (change.inserted() && !change.currentAuthor())) {
setChange(pos, Change(Change::DELETED));
return false;
}
- if (change.type == Change::DELETED)
+ if (change.deleted())
return false;
}
Font const & font,
Layout const & style)
{
- if (style.pass_thru || runparams.verbatim)
+ if (style.pass_thru || runparams.pass_thru)
return false;
if (i + 1 < int(text_.size())) {
}
}
- if (lyxrc.plaintext_linelen > 0
- && column > lyxrc.plaintext_linelen
+ if (runparams.linelen > 0
+ && column > runparams.linelen
&& i
&& text_[i - 1] != ' '
&& (i + 1 < int(text_.size()))
}
-void Paragraph::Private::latexInset(
- BufferParams const & bparams,
- odocstream & os,
- TexRow & texrow,
- OutputParams & runparams,
- Font & running_font,
- Font & basefont,
- Font const & outerfont,
- bool & open_font,
- Change & running_change,
- Layout const & style,
- pos_type & i,
- unsigned int & column)
+void Paragraph::Private::latexInset(BufferParams const & bparams,
+ odocstream & os,
+ TexRow & texrow,
+ OutputParams & runparams,
+ Font & running_font,
+ Font & basefont,
+ Font const & outerfont,
+ bool & open_font,
+ Change & running_change,
+ Layout const & style,
+ pos_type & i,
+ unsigned int & column)
{
Inset * inset = owner_->getInset(i);
LASSERT(inset, /**/);
column = 0;
}
- if (owner_->lookupChange(i).type == Change::DELETED) {
+ if (owner_->isDeleted(i)) {
if( ++runparams.inDeletedInset == 1)
runparams.changeOfDeletedInset = owner_->lookupChange(i);
}
if (inset->canTrackChanges()) {
column += Changes::latexMarkChange(os, bparams, running_change,
- Change(Change::UNCHANGED));
+ Change(Change::UNCHANGED), runparams);
running_change = Change(Change::UNCHANGED);
}
}
if (tmp) {
- for (int j = 0; j < tmp; ++j)
- texrow.newline();
-
+ texrow.newlines(tmp);
texrow.start(owner_->id(), i + 1);
column = 0;
} else {
column += os.tellp() - len;
}
- if (owner_->lookupChange(i).type == Change::DELETED)
+ if (owner_->isDeleted(i))
--runparams.inDeletedInset;
}
{
char_type const c = text_[i];
- if (style.pass_thru) {
+ if (style.pass_thru || runparams.pass_thru) {
if (c != '\0')
// FIXME UNICODE: This can fail if c cannot
// be encoded in the current encoding.
return;
}
- if (runparams.verbatim) {
- // FIXME UNICODE: This can fail if c cannot
- // be encoded in the current encoding.
- os.put(c);
- return;
- }
-
// If T1 font encoding is used, use the special
// characters it provides.
// NOTE: some languages reset the font encoding
}
-void Paragraph::Private::validate(LaTeXFeatures & features,
- Layout const & layout) const
-{
+void Paragraph::Private::validate(LaTeXFeatures & features) const
+{
+ if (layout_->inpreamble && inset_owner_) {
+ Buffer const & buf = inset_owner_->buffer();
+ BufferParams const & bp = buf.params();
+ Font f;
+ TexRow tr;
+ odocstringstream ods;
+ // we have to provide all the optional arguments here, even though
+ // the last one is the only one we care about.
+ owner_->latex(bp, f, ods, tr, features.runparams(), 0, -1, true);
+ docstring const d = ods.str();
+ if (!d.empty()) {
+ // this will have "{" at the beginning, but not at the end
+ string const content = to_utf8(d);
+ string const cmd = layout_->latexname();
+ features.addPreambleSnippet("\\" + cmd + content + "}");
+ }
+ }
+
+ if (features.runparams().flavor == OutputParams::HTML
+ && layout_->htmltitle()) {
+ features.setHTMLTitle(owner_->asString(AS_STR_INSETS));
+ }
+
// check the params.
if (!params_.spacing().isDefault())
features.require("setspace");
// then the layouts
- features.useLayout(layout.name());
+ features.useLayout(layout_->name());
// then the fonts
fontlist_.validate(features);
for (; icit != iend; ++icit) {
if (icit->inset) {
icit->inset->validate(features);
- if (layout.needprotect &&
+ if (layout_->needprotect &&
icit->inset->lyxCode() == FOOT_CODE)
features.require("NeedLyXFootnoteCode");
}
}
+namespace {
+
+// this shall be called just before every "os << ..." action.
+void flushString(ostream & os, docstring & s)
+{
+ os << to_utf8(s);
+ s.erase();
+}
+
+}
+
+
void Paragraph::write(ostream & os, BufferParams const & bparams,
depth_type & dth) const
{
Change running_change = Change(Change::UNCHANGED);
+ // this string is used as a buffer to avoid repetitive calls
+ // to to_utf8(), which turn out to be expensive (JMarc)
+ docstring write_buffer;
+
int column = 0;
for (pos_type i = 0; i <= size(); ++i) {
Change const change = lookupChange(i);
- Changes::lyxMarkChange(os, column, running_change, change);
+ if (change != running_change)
+ flushString(os, write_buffer);
+ Changes::lyxMarkChange(os, bparams, column, running_change, change);
running_change = change;
if (i == size())
Font font2 = getFontSettings(bparams, i);
font2.setMisspelled(false);
if (font2 != font1) {
+ flushString(os, write_buffer);
font2.lyxWriteChanges(font1, os);
column = 0;
font1 = font2;
switch (c) {
case META_INSET:
if (Inset const * inset = getInset(i)) {
+ flushString(os, write_buffer);
if (inset->directWrite()) {
// international char, let it write
// code directly so it's shorter in
}
break;
case '\\':
+ flushString(os, write_buffer);
os << "\n\\backslash\n";
column = 0;
break;
case '.':
+ flushString(os, write_buffer);
if (i + 1 < size() && d->text_[i + 1] == ' ') {
os << ".\n";
column = 0;
default:
if ((column > 70 && c == ' ')
|| column > 79) {
+ flushString(os, write_buffer);
os << '\n';
column = 0;
}
// this check is to amend a bug. LyX sometimes
// inserts '\0' this could cause problems.
if (c != '\0')
- os << to_utf8(docstring(1, c));
+ write_buffer.push_back(c);
else
LYXERR0("NUL char in structure.");
++column;
}
}
+ flushString(os, write_buffer);
os << "\n\\end_layout\n";
}
void Paragraph::validate(LaTeXFeatures & features) const
{
- d->validate(features, *d->layout_);
+ d->validate(features);
}
}
-docstring Paragraph::expandLabel(Layout const & layout,
+docstring Paragraph::expandLabel(Layout const & layout,
+ BufferParams const & bparams) const
+{
+ return expandParagraphLabel(layout, bparams, true);
+}
+
+
+docstring Paragraph::expandDocBookLabel(Layout const & layout,
+ BufferParams const & bparams) const
+{
+ return expandParagraphLabel(layout, bparams, false);
+}
+
+
+docstring Paragraph::expandParagraphLabel(Layout const & layout,
BufferParams const & bparams, bool process_appendix) const
{
DocumentClass const & tclass = bparams.documentClass();
docstring parent(fmt, i + 1, j - i - 1);
docstring label = from_ascii("??");
if (tclass.hasLayout(parent))
- docstring label = expandLabel(tclass[parent], bparams,
+ docstring label = expandParagraphLabel(tclass[parent], bparams,
process_appendix);
fmt = docstring(fmt, 0, i) + label
+ docstring(fmt, j + 1, docstring::npos);
}
-bool Paragraph::forcePlainLayout() const
-{
- return inInset().forcePlainLayout();
-}
-
-
bool Paragraph::allowParagraphCustomization() const
{
return inInset().allowParagraphCustomization();
case LYX_ALIGN_BLOCK:
case LYX_ALIGN_LAYOUT:
case LYX_ALIGN_SPECIAL:
+ case LYX_ALIGN_DECIMAL:
break;
case LYX_ALIGN_LEFT:
case LYX_ALIGN_RIGHT:
}
string const begin_tag = "\\begin";
- InsetCode code = owner_->ownerCode();
+ InsetCode code = ownerCode();
bool const lastpar = runparams.isLastPar;
switch (curAlign) {
case LYX_ALIGN_BLOCK:
case LYX_ALIGN_LAYOUT:
case LYX_ALIGN_SPECIAL:
+ case LYX_ALIGN_DECIMAL:
break;
case LYX_ALIGN_LEFT: {
string output;
case LYX_ALIGN_BLOCK:
case LYX_ALIGN_LAYOUT:
case LYX_ALIGN_SPECIAL:
+ case LYX_ALIGN_DECIMAL:
break;
case LYX_ALIGN_LEFT:
case LYX_ALIGN_RIGHT:
}
string const end_tag = "\n\\par\\end";
- InsetCode code = owner_->ownerCode();
+ InsetCode code = ownerCode();
bool const lastpar = runparams.isLastPar;
switch (curAlign) {
case LYX_ALIGN_BLOCK:
case LYX_ALIGN_LAYOUT:
case LYX_ALIGN_SPECIAL:
+ case LYX_ALIGN_DECIMAL:
break;
case LYX_ALIGN_LEFT: {
string output;
// This one spits out the text of the paragraph
-bool Paragraph::latex(BufferParams const & bparams,
+void Paragraph::latex(BufferParams const & bparams,
Font const & outerfont,
odocstream & os, TexRow & texrow,
OutputParams const & runparams,
- int start_pos, int end_pos) const
+ int start_pos, int end_pos, bool force) const
{
LYXERR(Debug::LATEX, "Paragraph::latex... " << this);
- bool return_value = false;
-
- bool const allowcust = allowParagraphCustomization();
-
// FIXME This check should not be needed. Perhaps issue an
// error if it triggers.
- Layout const & style = forcePlainLayout() ?
+ Layout const & style = inInset().forcePlainLayout() ?
bparams.documentClass().plainLayout() : *d->layout_;
+ if (!force && style.inpreamble)
+ return;
+
+ bool const allowcust = allowParagraphCustomization();
+
// Current base font for all inherited font changes, without any
// change caused by an individual character, except for the language:
// It is set to the language of the first character.
unsigned int column = 0;
if (body_pos > 0) {
- os << '[';
- column += 1;
+ // the optional argument is kept in curly brackets in
+ // case it contains a ']'
+ os << "[{";
+ column += 2;
basefont = getLabelFont(bparams, outerfont);
} else {
basefont = getLayoutFont(bparams, outerfont);
Change runningChange = Change(Change::UNCHANGED);
+ Encoding const * const prev_encoding = runparams.encoding;
+
texrow.start(id(), 0);
// if the paragraph is empty, the loop will not be entered at all
running_font = basefont;
column += Changes::latexMarkChange(os, bparams,
- runningChange, Change(Change::UNCHANGED));
+ runningChange, Change(Change::UNCHANGED),
+ runparams);
runningChange = Change(Change::UNCHANGED);
- os << "] ";
- column +=2;
+ os << "}] ";
+ column +=3;
}
if (style.isCommand()) {
os << '{';
basefont = getLayoutFont(bparams, outerfont);
running_font = basefont;
- column += Changes::latexMarkChange(os, bparams, runningChange, change);
+ column += Changes::latexMarkChange(os, bparams, runningChange,
+ change, runparams);
runningChange = change;
}
// do not output text which is marked deleted
// if change tracking output is disabled
- if (!bparams.outputChanges && change.type == Change::DELETED) {
+ if (!bparams.outputChanges && change.deleted()) {
continue;
}
}
// Switch file encoding if necessary (and allowed)
- if (!runparams.verbatim &&
+ if (!runparams.pass_thru && !style.pass_thru &&
runparams.encoding->package() != Encoding::none &&
font.language()->encoding()->package() != Encoding::none) {
pair<bool, int> const enc_switch = switchEncoding(os, bparams,
running_font = font;
open_font = true;
docstring fontchange = ods.str();
+ // check whether the fontchange ends with a \\textcolor
+ // modifier and the text starts with a space (bug 4473)
+ docstring const last_modifier = rsplit(fontchange, '\\');
+ if (prefixIs(last_modifier, from_ascii("textcolor")) && c == ' ')
+ os << fontchange << from_ascii("{}");
// check if the fontchange ends with a trailing blank
// (like "\small " (see bug 3382)
- if (suffixIs(fontchange, ' ') && c == ' ')
+ else if (suffixIs(fontchange, ' ') && c == ' ')
os << fontchange.substr(0, fontchange.size() - 1)
<< from_ascii("{}");
else
if (open_font) {
#ifdef FIXED_LANGUAGE_END_DETECTION
if (next_) {
- running_font
- .latexWriteEndChanges(os, bparams, runparams,
- basefont,
+ running_font.latexWriteEndChanges(os, bparams,
+ runparams, basefont,
next_->getFont(bparams, 0, outerfont));
} else {
running_font.latexWriteEndChanges(os, bparams,
#endif
}
- column += Changes::latexMarkChange(os, bparams, runningChange, Change(Change::UNCHANGED));
+ column += Changes::latexMarkChange(os, bparams, runningChange,
+ Change(Change::UNCHANGED), runparams);
// Needed if there is an optional argument but no contents.
if (body_pos > 0 && body_pos == size()) {
- os << "]~";
- return_value = false;
+ os << "}]~";
}
- if (allowcust) {
- column += d->endTeXParParams(bparams, os, texrow,
- runparams);
+ if (allowcust && d->endTeXParParams(bparams, os, texrow, runparams)
+ && runparams.encoding != prev_encoding) {
+ runparams.encoding = prev_encoding;
+ if (!bparams.useXetex)
+ os << setEncoding(prev_encoding->iconvName());
}
LYXERR(Debug::LATEX, "Paragraph::latex... done " << this);
- return return_value;
}
}
-pos_type Paragraph::firstWordLyXHTML(odocstream & os, OutputParams const & runparams)
+pos_type Paragraph::firstWordLyXHTML(XHTMLStream & xs, OutputParams const & runparams)
const
{
pos_type i;
for (i = 0; i < size(); ++i) {
if (Inset const * inset = getInset(i)) {
- inset->xhtml(os, runparams);
+ inset->xhtml(xs, runparams);
} else {
char_type c = d->text_[i];
if (c == ' ')
break;
- os << html::escapeChar(c);
+ xs << c;
}
}
return i;
docstring Paragraph::simpleLyXHTMLOnePar(Buffer const & buf,
- odocstream & os,
+ XHTMLStream & xs,
OutputParams const & runparams,
Font const & outerfont,
pos_type initial) const
{
docstring retval;
- // FIXME We really need to manage the tag nesting here.
- // Probably in the same sort of way as in output_xhtml.
bool emph_flag = false;
bool bold_flag = false;
- std::string closing_tag;
+ string closing_tag;
Layout const & style = *d->layout_;
+
+ if (!runparams.for_toc && runparams.html_make_pars) {
+ // generate a magic label for this paragraph
+ string const attr = "id='" + magicLabel() + "'";
+ xs << html::CompTag("a", attr);
+ }
+
FontInfo font_old =
style.labeltype == LABEL_MANUAL ? style.labelfont : style.font;
- //if (style.pass_thru && !d->onlyText(buf, outerfont, initial))
- // os << "]]>";
-
// parsing main loop
for (pos_type i = initial; i < size(); ++i) {
+ // let's not show deleted material in the output
+ if (isDeleted(i))
+ continue;
+
Font font = getFont(buf.params(), i, outerfont);
// emphasis
if (font_old.emph() != font.fontInfo().emph()) {
if (font.fontInfo().emph() == FONT_ON) {
- os << "<em>";
+ xs << html::StartTag("em");
emph_flag = true;
} else if (emph_flag && i != initial) {
- os << "</em>";
+ xs << html::EndTag("em");
emph_flag = false;
}
}
// bold
if (font_old.series() != font.fontInfo().series()) {
if (font.fontInfo().series() == BOLD_SERIES) {
- os << "<strong>";
+ xs << html::StartTag("strong");
bold_flag = true;
} else if (bold_flag && i != initial) {
- os << "</strong>";
+ xs << html::EndTag("strong");
bold_flag = false;
}
}
- // FIXME Other such tags?
-
- if (Inset const * inset = getInset(i)) {
- retval += inset->xhtml(os, runparams);
+ // FIXME XHTML
+ // Other such tags? What about the other text ranges?
+
+ Inset const * inset = getInset(i);
+ if (inset) {
+ InsetCommand const * ic = inset->asInsetCommand();
+ InsetLayout const & il = inset->getLayout();
+ InsetMath const * im = inset->asInsetMath();
+ if (!runparams.for_toc
+ || im || il.isInToc() || (ic && ic->isInToc())) {
+ OutputParams np = runparams;
+ if (!il.htmlisblock())
+ np.html_in_par = true;
+ retval += inset->xhtml(xs, np);
+ }
} else {
char_type c = d->text_[i];
if (style.pass_thru)
- os.put(c);
+ xs << c;
else if (c == '-') {
docstring str;
int j = i + 1;
}
else
str += c;
- os << str;
+ // We don't want to escape the entities. Note that
+ // it is safe to do this, since str can otherwise
+ // only be "-". E.g., it can't be "<".
+ xs << XHTMLStream::NextRaw() << str;
} else
- os << html::escapeChar(c);
+ xs << c;
}
font_old = font.fontInfo();
}
- // FIXME This could be out of order. See above.
- if (emph_flag)
- os << "</em>";
- if (bold_flag)
- os << "</strong>";
-
+ xs.closeFontTags();
return retval;
}
bool Paragraph::isMultiLingual(BufferParams const & bparams) const
{
- Language const * doc_language = bparams.language;
+ Language const * doc_language = bparams.language;
FontList::const_iterator cit = d->fontlist_.begin();
FontList::const_iterator end = d->fontlist_.end();
}
+void Paragraph::getLanguages(std::set<Language const *> & languages) const
+{
+ FontList::const_iterator cit = d->fontlist_.begin();
+ FontList::const_iterator end = d->fontlist_.end();
+
+ for (; cit != end; ++cit) {
+ Language const * lang = cit->font().language();
+ if (lang != ignore_language &&
+ lang != latex_language)
+ languages.insert(lang);
+ }
+}
+
+
docstring Paragraph::asString(int options) const
{
return asString(0, size(), options);
odocstringstream os;
if (beg == 0
- && options & AS_STR_LABEL
- && !d->params_.labelString().empty())
+ && options & AS_STR_LABEL
+ && !d->params_.labelString().empty())
os << d->params_.labelString() << ' ';
for (pos_type i = beg; i < end; ++i) {
char_type const c = d->text_[i];
if (isPrintable(c) || c == '\t'
- || (c == '\n' && options & AS_STR_NEWLINES))
+ || (c == '\n' && (options & AS_STR_NEWLINES)))
os.put(c);
- else if (c == META_INSET && options & AS_STR_INSETS)
+ else if (c == META_INSET && (options & AS_STR_INSETS)) {
getInset(i)->tocString(os);
+ if (getInset(i)->asInsetMath())
+ os << " ";
+ }
}
return os.str();
for (pos_type i = beg; i < end; ++i) {
char_type const c = d->text_[i];
if (isPrintable(c) || c == '\t'
- || (c == '\n' && options & AS_STR_NEWLINES))
+ || (c == '\n' && (options & AS_STR_NEWLINES)))
os.put(c);
- else if (c == META_INSET && options & AS_STR_INSETS) {
+ else if (c == META_INSET && (options & AS_STR_INSETS)) {
getInset(i)->plaintext(os, runparams);
}
}
}
+void Paragraph::setId(int id)
+{
+ d->id_ = id;
+}
+
+
Layout const & Paragraph::layout() const
{
return *d->layout_;
}
-InsetCode Paragraph::ownerCode() const
-{
- return d->inset_owner_ ? d->inset_owner_->lyxCode() : NO_CODE;
-}
-
-
ParagraphParameters & Paragraph::params()
{
return d->params_;
// There was no inset at the beginning, so we need to create one with
// the key and label of the one we erased.
InsetBibitem * inset =
- new InsetBibitem(buffer, InsetCommandParams(BIBITEM_CODE));
+ new InsetBibitem(const_cast<Buffer *>(&buffer), InsetCommandParams(BIBITEM_CODE));
// restore values of previously deleted item in this par.
if (!oldkey.empty())
inset->setParam("key", oldkey);
}
-bool Paragraph::isUnchanged(pos_type pos) const
+bool Paragraph::isChanged(pos_type pos) const
{
- return lookupChange(pos).type == Change::UNCHANGED;
+ return lookupChange(pos).changed();
}
bool Paragraph::isInserted(pos_type pos) const
{
- return lookupChange(pos).type == Change::INSERTED;
+ return lookupChange(pos).inserted();
}
bool Paragraph::isDeleted(pos_type pos) const
{
- return lookupChange(pos).type == Change::DELETED;
+ return lookupChange(pos).deleted();
}
void Paragraph::deregisterWords()
{
- Private::Words::const_iterator it;
- WordList & wl = theWordList();
- for (it = d->words_.begin(); it != d->words_.end(); ++it)
- wl.remove(*it);
+ Private::LangWordsMap::const_iterator itl = d->words_.begin();
+ Private::LangWordsMap::const_iterator ite = d->words_.end();
+ for (; itl != ite; ++itl) {
+ WordList * wl = theWordList(itl->first);
+ Private::Words::const_iterator it = (itl->second).begin();
+ Private::Words::const_iterator et = (itl->second).end();
+ for (; it != et; ++it)
+ wl->remove(*it);
+ }
d->words_.clear();
}
void Paragraph::collectWords()
{
+ // This is the value that needs to be exposed in the preferences
+ // to resolve bug #6760.
+ static int minlength = 6;
pos_type n = size();
- WordLangTuple wl;
- docstring_list suggestions;
for (pos_type pos = 0; pos < n; ++pos) {
if (isWordSeparator(pos))
continue;
pos_type from = pos;
locateWord(from, pos, WHOLE_WORD);
- if (pos - from >= 6) {
+ if (pos - from >= minlength) {
docstring word = asString(from, pos, AS_STR_NONE);
- d->words_.insert(word);
- }
- if (lyxrc.spellcheck_continuously
- && spellCheck(from, pos, wl, suggestions)) {
- for (size_t i = 0; i != suggestions.size(); ++i)
- d->words_.insert(suggestions[i]);
+ FontList::const_iterator cit = d->fontlist_.fontIterator(pos);
+ if (cit == d->fontlist_.end())
+ return;
+ Language const * lang = cit->font().language();
+ d->words_[*lang].insert(word);
}
}
}
void Paragraph::registerWords()
{
- Private::Words::const_iterator it;
- WordList & wl = theWordList();
- for (it = d->words_.begin(); it != d->words_.end(); ++it)
- wl.insert(*it);
+ Private::LangWordsMap::const_iterator itl = d->words_.begin();
+ Private::LangWordsMap::const_iterator ite = d->words_.end();
+ for (; itl != ite; ++itl) {
+ WordList * wl = theWordList(itl->first);
+ Private::Words::const_iterator it = (itl->second).begin();
+ Private::Words::const_iterator et = (itl->second).end();
+ for (; it != et; ++it)
+ wl->insert(*it);
+ }
}
}
-bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
- docstring_list & suggestions) const
+SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
+ docstring_list & suggestions, bool do_suggestion) const
{
SpellChecker * speller = theSpellChecker();
if (!speller)
- return false;
+ return SpellChecker::WORD_OK;
+
+ if (!d->layout_->spellcheck || !inInset().allowSpellCheck())
+ return SpellChecker::WORD_OK;
locateWord(from, to, WHOLE_WORD);
if (from == to || from >= pos_type(d->text_.size()))
- return false;
+ return SpellChecker::WORD_OK;
docstring word = asString(from, to, AS_STR_INSETS);
- string const lang_code = lyxrc.spellchecker_alt_lang.empty()
- ? getFontSettings(d->inset_owner_->buffer().params(), from).language()->code()
- : lyxrc.spellchecker_alt_lang;
- wl = WordLangTuple(word, lang_code);
- SpellChecker::Result res = speller->check(wl);
- // Just ignore any error that the spellchecker reports.
- // FIXME: we should through out an exception and catch it in the GUI to
- // display the error.
- if (!speller->error().empty())
- return false;
-
- bool const misspelled = res != SpellChecker::OK
- && res != SpellChecker::IGNORED_WORD;
+ // Ignore words with digits
+ // FIXME: make this customizable
+ // (note that hunspell ignores words with digits by default)
+ bool const ignored = hasDigit(word);
+ Language * lang = const_cast<Language *>(getFontSettings(
+ d->inset_owner_->buffer().params(), from).language());
+ if (lang == d->inset_owner_->buffer().params().language
+ && !lyxrc.spellchecker_alt_lang.empty()) {
+ string lang_code;
+ string const lang_variety =
+ split(lyxrc.spellchecker_alt_lang, lang_code, '-');
+ lang->setCode(lang_code);
+ lang->setVariety(lang_variety);
+ }
+ wl = WordLangTuple(word, lang);
+ SpellChecker::Result res = ignored ?
+ SpellChecker::WORD_OK : speller->check(wl);
+
+ bool const misspelled_ = SpellChecker::misspelled(res) ;
if (lyxrc.spellcheck_continuously)
- d->fontlist_.setMisspelled(from, to, misspelled);
+ d->fontlist_.setMisspelled(from, to, misspelled_);
- if (misspelled) {
- while (!(word = speller->nextMiss()).empty())
- suggestions.push_back(word);
- }
- return misspelled;
+ if (misspelled_ && do_suggestion)
+ speller->suggest(wl, suggestions);
+ else
+ suggestions.clear();
+
+ return res;
}
pos_type to = pos;
WordLangTuple wl;
docstring_list suggestions;
- return spellCheck(from, to, wl, suggestions);
+ SpellChecker::Result res = spellCheck(from, to, wl, suggestions, false);
+ return SpellChecker::misspelled(res) ;
+}
+
+
+string Paragraph::magicLabel() const
+{
+ stringstream ss;
+ ss << "magicparlabel-" << id();
+ return ss.str();
}