3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
13 * \author Jürgen Vigna
15 * Full author contact details are available in file CREDITS.
20 #include "Paragraph.h"
22 #include "LayoutFile.h"
24 #include "BufferParams.h"
28 #include "InsetList.h"
30 #include "LaTeXFeatures.h"
36 #include "OutputParams.h"
37 #include "output_latex.h"
38 #include "paragraph_funcs.h"
39 #include "ParagraphParameters.h"
41 #include "TextClass.h"
47 #include "frontends/alert.h"
49 #include "insets/InsetBibitem.h"
50 #include "insets/InsetLabel.h"
52 #include "support/lassert.h"
53 #include "support/convert.h"
54 #include "support/debug.h"
55 #include "support/ExceptionMessage.h"
56 #include "support/gettext.h"
57 #include "support/lstrings.h"
58 #include "support/Messages.h"
59 #include "support/textutils.h"
65 using namespace lyx::support
;
70 /// Inset identifier (above 0x10ffff, for ucs-4)
71 char_type
const META_INSET
= 0x200001;
74 /////////////////////////////////////////////////////////////////////
78 /////////////////////////////////////////////////////////////////////
80 class Paragraph::Private
84 Private(Paragraph
* owner
, Layout
const & layout
);
85 /// "Copy constructor"
86 Private(Private
const &, Paragraph
* owner
);
87 /// Copy constructor from \p beg to \p end
88 Private(Private
const &, Paragraph
* owner
, pos_type beg
, pos_type end
);
91 void insertChar(pos_type pos
, char_type c
, Change
const & change
);
93 /// Output the surrogate pair formed by \p c and \p next to \p os.
94 /// \return the number of characters written.
95 int latexSurrogatePair(odocstream
& os
, char_type c
, char_type next
,
98 /// Output a space in appropriate formatting (or a surrogate pair
99 /// if the next character is a combining character).
100 /// \return whether a surrogate pair was output.
101 bool simpleTeXBlanks(OutputParams
const &,
102 odocstream
&, TexRow
& texrow
,
104 unsigned int & column
,
106 Layout
const & style
);
108 /// Output consecutive unicode chars, belonging to the same script as
109 /// specified by the latex macro \p ltx, to \p os starting from \p i.
110 /// \return the number of characters written.
111 int writeScriptChars(odocstream
& os
, docstring
const & ltx
,
112 Change
const &, Encoding
const &, pos_type
& i
);
114 /// This could go to ParagraphParameters if we want to.
115 int startTeXParParams(BufferParams
const &, odocstream
&, TexRow
&,
116 OutputParams
const &) const;
118 /// This could go to ParagraphParameters if we want to.
119 int endTeXParParams(BufferParams
const &, odocstream
&, TexRow
&,
120 OutputParams
const &) const;
123 void latexInset(BufferParams
const &,
125 TexRow
& texrow
, OutputParams
&,
128 Font
const & outerfont
,
130 Change
& running_change
,
131 Layout
const & style
,
133 unsigned int & column
);
136 void latexSpecialChar(
138 OutputParams
const & runparams
,
139 Font
const & running_font
,
140 Change
const & running_change
,
141 Layout
const & style
,
143 unsigned int & column
);
150 unsigned int & column
);
152 bool latexSpecialTypewriter(
156 unsigned int & column
);
158 bool latexSpecialPhrase(
161 unsigned int & column
,
162 OutputParams
const & runparams
);
165 void validate(LaTeXFeatures
& features
,
166 Layout
const & layout
) const;
168 /// Checks if the paragraph contains only text and no inset or font change.
169 bool onlyText(Buffer
const & buf
, Font
const & outerfont
,
170 pos_type initial
) const;
172 /// match a string against a particular point in the paragraph
173 bool isTextAt(string
const & str
, pos_type pos
) const;
175 /// Which Paragraph owns us?
179 Inset
const * inset_owner_
;
187 static unsigned int paragraph_id
;
189 ParagraphParameters params_
;
191 /// for recording and looking up changes
195 InsetList insetlist_
;
198 pos_type begin_of_body_
;
200 typedef docstring TextContainer
;
204 typedef std::set
<docstring
> Words
;
208 Layout
const * layout_
;
212 // Initialization of the counter for the paragraph id's,
213 unsigned int Paragraph::Private::paragraph_id
= 0;
217 struct special_phrase
{
223 special_phrase
const special_phrases
[] = {
224 { "LyX", from_ascii("\\LyX{}"), false },
225 { "TeX", from_ascii("\\TeX{}"), true },
226 { "LaTeX2e", from_ascii("\\LaTeXe{}"), true },
227 { "LaTeX", from_ascii("\\LaTeX{}"), true },
230 size_t const phrases_nr
= sizeof(special_phrases
)/sizeof(special_phrase
);
235 Paragraph::Private::Private(Paragraph
* owner
, Layout
const & layout
)
236 : owner_(owner
), inset_owner_(0), begin_of_body_(0), layout_(&layout
)
238 id_
= paragraph_id
++;
243 Paragraph::Private::Private(Private
const & p
, Paragraph
* owner
)
244 : owner_(owner
), inset_owner_(p
.inset_owner_
), fontlist_(p
.fontlist_
),
245 params_(p
.params_
), changes_(p
.changes_
), insetlist_(p
.insetlist_
),
246 begin_of_body_(p
.begin_of_body_
), text_(p
.text_
), words_(p
.words_
),
249 id_
= paragraph_id
++;
253 Paragraph::Private::Private(Private
const & p
, Paragraph
* owner
,
254 pos_type beg
, pos_type end
)
255 : owner_(owner
), inset_owner_(p
.inset_owner_
),
256 params_(p
.params_
), changes_(p
.changes_
),
257 insetlist_(p
.insetlist_
, beg
, end
),
258 begin_of_body_(p
.begin_of_body_
), words_(p
.words_
),
261 id_
= paragraph_id
++;
262 if (beg
>= pos_type(p
.text_
.size()))
264 text_
= p
.text_
.substr(beg
, end
- beg
);
266 FontList::const_iterator fcit
= fontlist_
.begin();
267 FontList::const_iterator fend
= fontlist_
.end();
268 for (; fcit
!= fend
; ++fcit
) {
269 if (fcit
->pos() < beg
)
271 if (fcit
->pos() >= end
) {
272 // Add last entry in the fontlist_.
273 fontlist_
.set(text_
.size() - 1, fcit
->font());
276 // Add a new entry in the fontlist_.
277 fontlist_
.set(fcit
->pos() - beg
, fcit
->font());
282 void Paragraph::addChangesToToc(DocIterator
const & cdit
,
283 Buffer
const & buf
) const
285 d
->changes_
.addToToc(cdit
, buf
);
289 bool Paragraph::isChanged(pos_type start
, pos_type end
) const
291 LASSERT(start
>= 0 && start
<= size(), /**/);
292 LASSERT(end
> start
&& end
<= size() + 1, /**/);
294 return d
->changes_
.isChanged(start
, end
);
298 bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges
) const
300 // keep the logic here in sync with the logic of eraseChars()
304 Change
const change
= d
->changes_
.lookup(size());
305 return change
.type
== Change::INSERTED
&& change
.author
== 0;
309 void Paragraph::setChange(Change
const & change
)
311 // beware of the imaginary end-of-par character!
312 d
->changes_
.set(change
, 0, size() + 1);
315 * Propagate the change recursively - but not in case of DELETED!
317 * Imagine that your co-author makes changes in an existing inset. He
318 * sends your document to you and you come to the conclusion that the
319 * inset should go completely. If you erase it, LyX must not delete all
320 * text within the inset. Otherwise, the change tracked insertions of
321 * your co-author get lost and there is no way to restore them later.
323 * Conclusion: An inset's content should remain untouched if you delete it
326 if (change
.type
!= Change::DELETED
) {
327 for (pos_type pos
= 0; pos
< size(); ++pos
) {
328 if (Inset
* inset
= getInset(pos
))
329 inset
->setChange(change
);
335 void Paragraph::setChange(pos_type pos
, Change
const & change
)
337 LASSERT(pos
>= 0 && pos
<= size(), /**/);
338 d
->changes_
.set(change
, pos
);
340 // see comment in setChange(Change const &) above
341 if (change
.type
!= Change::DELETED
&& pos
< size())
342 if (Inset
* inset
= getInset(pos
))
343 inset
->setChange(change
);
347 Change
const & Paragraph::lookupChange(pos_type pos
) const
349 LASSERT(pos
>= 0 && pos
<= size(), /**/);
350 return d
->changes_
.lookup(pos
);
354 void Paragraph::acceptChanges(BufferParams
const & bparams
, pos_type start
,
357 LASSERT(start
>= 0 && start
<= size(), /**/);
358 LASSERT(end
> start
&& end
<= size() + 1, /**/);
360 for (pos_type pos
= start
; pos
< end
; ++pos
) {
361 switch (lookupChange(pos
).type
) {
362 case Change::UNCHANGED
:
363 // accept changes in nested inset
364 if (Inset
* inset
= getInset(pos
))
365 inset
->acceptChanges(bparams
);
368 case Change::INSERTED
:
369 d
->changes_
.set(Change(Change::UNCHANGED
), pos
);
370 // also accept changes in nested inset
371 if (Inset
* inset
= getInset(pos
))
372 inset
->acceptChanges(bparams
);
375 case Change::DELETED
:
376 // Suppress access to non-existent
377 // "end-of-paragraph char"
379 eraseChar(pos
, false);
390 void Paragraph::rejectChanges(BufferParams
const & bparams
,
391 pos_type start
, pos_type end
)
393 LASSERT(start
>= 0 && start
<= size(), /**/);
394 LASSERT(end
> start
&& end
<= size() + 1, /**/);
396 for (pos_type pos
= start
; pos
< end
; ++pos
) {
397 switch (lookupChange(pos
).type
) {
398 case Change::UNCHANGED
:
399 // reject changes in nested inset
400 if (Inset
* inset
= getInset(pos
))
401 inset
->rejectChanges(bparams
);
404 case Change::INSERTED
:
405 // Suppress access to non-existent
406 // "end-of-paragraph char"
408 eraseChar(pos
, false);
414 case Change::DELETED
:
415 d
->changes_
.set(Change(Change::UNCHANGED
), pos
);
417 // Do NOT reject changes within a deleted inset!
418 // There may be insertions of a co-author inside of it!
426 void Paragraph::Private::insertChar(pos_type pos
, char_type c
,
427 Change
const & change
)
429 LASSERT(pos
>= 0 && pos
<= int(text_
.size()), /**/);
432 changes_
.insert(change
, pos
);
434 // This is actually very common when parsing buffers (and
435 // maybe inserting ascii text)
436 if (pos
== pos_type(text_
.size())) {
437 // when appending characters, no need to update tables
442 text_
.insert(text_
.begin() + pos
, c
);
444 // Update the font table.
445 fontlist_
.increasePosAfterPos(pos
);
448 insetlist_
.increasePosAfterPos(pos
);
452 bool Paragraph::insertInset(pos_type pos
, Inset
* inset
,
453 Change
const & change
)
455 LASSERT(inset
, /**/);
456 LASSERT(pos
>= 0 && pos
<= size(), /**/);
458 // Paragraph::insertInset() can be used in cut/copy/paste operation where
459 // d->inset_owner_ is not set yet.
460 if (d
->inset_owner_
&& !d
->inset_owner_
->insetAllowed(inset
->lyxCode()))
463 d
->insertChar(pos
, META_INSET
, change
);
464 LASSERT(d
->text_
[pos
] == META_INSET
, /**/);
466 // Add a new entry in the insetlist_.
467 d
->insetlist_
.insert(inset
, pos
);
472 bool Paragraph::eraseChar(pos_type pos
, bool trackChanges
)
474 LASSERT(pos
>= 0 && pos
<= size(), return false);
476 // keep the logic here in sync with the logic of isMergedOnEndOfParDeletion()
479 Change change
= d
->changes_
.lookup(pos
);
481 // set the character to DELETED if
482 // a) it was previously unchanged or
483 // b) it was inserted by a co-author
485 if (change
.type
== Change::UNCHANGED
||
486 (change
.type
== Change::INSERTED
&& change
.author
!= 0)) {
487 setChange(pos
, Change(Change::DELETED
));
491 if (change
.type
== Change::DELETED
)
495 // Don't physically access the imaginary end-of-paragraph character.
496 // eraseChar() can only mark it as DELETED. A physical deletion of
497 // end-of-par must be handled externally.
503 d
->changes_
.erase(pos
);
505 // if it is an inset, delete the inset entry
506 if (d
->text_
[pos
] == META_INSET
)
507 d
->insetlist_
.erase(pos
);
509 d
->text_
.erase(d
->text_
.begin() + pos
);
511 // Update the fontlist_
512 d
->fontlist_
.erase(pos
);
514 // Update the insetlist_
515 d
->insetlist_
.decreasePosAfterPos(pos
);
521 int Paragraph::eraseChars(pos_type start
, pos_type end
, bool trackChanges
)
523 LASSERT(start
>= 0 && start
<= size(), /**/);
524 LASSERT(end
>= start
&& end
<= size() + 1, /**/);
527 for (pos_type count
= end
- start
; count
; --count
) {
528 if (!eraseChar(i
, trackChanges
))
535 int Paragraph::Private::latexSurrogatePair(odocstream
& os
, char_type c
,
536 char_type next
, Encoding
const & encoding
)
538 // Writing next here may circumvent a possible font change between
539 // c and next. Since next is only output if it forms a surrogate pair
540 // with c we can ignore this:
541 // A font change inside a surrogate pair does not make sense and is
542 // hopefully impossible to input.
543 // FIXME: change tracking
544 // Is this correct WRT change tracking?
545 docstring
const latex1
= encoding
.latexChar(next
);
546 docstring
const latex2
= encoding
.latexChar(c
);
547 if (docstring(1, next
) == latex1
) {
548 // the encoding supports the combination
549 os
<< latex2
<< latex1
;
550 return latex1
.length() + latex2
.length();
552 os
<< latex1
<< '{' << latex2
<< '}';
553 return latex1
.length() + latex2
.length() + 2;
557 bool Paragraph::Private::simpleTeXBlanks(OutputParams
const & runparams
,
558 odocstream
& os
, TexRow
& texrow
,
560 unsigned int & column
,
562 Layout
const & style
)
564 if (style
.pass_thru
|| runparams
.verbatim
)
567 if (i
+ 1 < int(text_
.size())) {
568 char_type next
= text_
[i
+ 1];
569 if (Encodings::isCombiningChar(next
)) {
570 Encoding
const & encoding
= *(runparams
.encoding
);
571 // This space has an accent, so we must always output it.
572 column
+= latexSurrogatePair(os
, ' ', next
, encoding
) - 1;
577 if (lyxrc
.plaintext_linelen
> 0
578 && column
> lyxrc
.plaintext_linelen
580 && text_
[i
- 1] != ' '
581 && (i
+ 1 < int(text_
.size()))
582 // same in FreeSpacing mode
583 && !owner_
->isFreeSpacing()
584 // In typewriter mode, we want to avoid
585 // ! . ? : at the end of a line
586 && !(font
.fontInfo().family() == TYPEWRITER_FAMILY
587 && (text_
[i
- 1] == '.'
588 || text_
[i
- 1] == '?'
589 || text_
[i
- 1] == ':'
590 || text_
[i
- 1] == '!'))) {
593 texrow
.start(owner_
->id(), i
+ 1);
595 } else if (style
.free_spacing
) {
604 int Paragraph::Private::writeScriptChars(odocstream
& os
,
605 docstring
const & ltx
,
606 Change
const & runningChange
,
607 Encoding
const & encoding
,
610 // FIXME: modifying i here is not very nice...
612 // We only arrive here when a proper language for character text_[i] has
613 // not been specified (i.e., it could not be translated in the current
614 // latex encoding) or its latex translation has been forced, and it
615 // belongs to a known script.
616 // Parameter ltx contains the latex translation of text_[i] as specified
617 // in the unicodesymbols file and is something like "\textXXX{<spec>}".
618 // The latex macro name "textXXX" specifies the script to which text_[i]
619 // belongs and we use it in order to check whether characters from the
620 // same script immediately follow, such that we can collect them in a
621 // single "\textXXX" macro. So, we have to retain "\textXXX{<spec>"
622 // for the first char but only "<spec>" for all subsequent chars.
623 docstring::size_type
const brace1
= ltx
.find_first_of(from_ascii("{"));
624 docstring::size_type
const brace2
= ltx
.find_last_of(from_ascii("}"));
625 string script
= to_ascii(ltx
.substr(1, brace1
- 1));
628 bool closing_brace
= true;
629 if (script
== "textgreek" && encoding
.latexName() == "iso-8859-7") {
630 // Correct encoding is being used, so we can avoid \textgreek.
633 closing_brace
= false;
635 os
<< ltx
.substr(pos
, length
);
636 int size
= text_
.size();
637 while (i
+ 1 < size
) {
638 char_type
const next
= text_
[i
+ 1];
639 // Stop here if next character belongs to another script
640 // or there is a change in change tracking status.
641 if (!Encodings::isKnownScriptChar(next
, script
) ||
642 runningChange
!= owner_
->lookupChange(i
+ 1))
646 FontList::const_iterator cit
= fontlist_
.begin();
647 FontList::const_iterator end
= fontlist_
.end();
648 for (; cit
!= end
; ++cit
) {
649 if (cit
->pos() >= i
&& !found
) {
650 prev_font
= cit
->font();
653 if (cit
->pos() >= i
+ 1)
656 // Stop here if there is a font attribute or encoding change.
657 if (found
&& cit
!= end
&& prev_font
!= cit
->font())
659 docstring
const latex
= encoding
.latexChar(next
);
660 docstring::size_type
const b1
=
661 latex
.find_first_of(from_ascii("{"));
662 docstring::size_type
const b2
=
663 latex
.find_last_of(from_ascii("}"));
664 int const len
= b2
- b1
- 1;
665 os
<< latex
.substr(b1
+ 1, len
);
677 bool Paragraph::Private::isTextAt(string
const & str
, pos_type pos
) const
679 pos_type
const len
= str
.length();
681 // is the paragraph large enough?
682 if (pos
+ len
> int(text_
.size()))
685 // does the wanted text start at point?
686 for (string::size_type i
= 0; i
< str
.length(); ++i
) {
687 // Caution: direct comparison of characters works only
688 // because str is pure ASCII.
689 if (str
[i
] != text_
[pos
+ i
])
693 return fontlist_
.hasChangeInRange(pos
, len
);
697 void Paragraph::Private::latexInset(
698 BufferParams
const & bparams
,
701 OutputParams
& runparams
,
704 Font
const & outerfont
,
706 Change
& running_change
,
707 Layout
const & style
,
709 unsigned int & column
)
711 Inset
* inset
= owner_
->getInset(i
);
712 LASSERT(inset
, /**/);
714 if (style
.pass_thru
) {
715 inset
->plaintext(os
, runparams
);
719 // FIXME: move this to InsetNewline::latex
720 if (inset
->lyxCode() == NEWLINE_CODE
) {
721 // newlines are handled differently here than
722 // the default in simpleTeXSpecialChars().
723 if (!style
.newline_allowed
) {
727 column
+= running_font
.latexWriteEndChanges(
728 os
, bparams
, runparams
,
733 if (running_font
.fontInfo().family() == TYPEWRITER_FAMILY
)
736 basefont
= owner_
->getLayoutFont(bparams
, outerfont
);
737 running_font
= basefont
;
739 if (runparams
.moving_arg
)
744 texrow
.start(owner_
->id(), i
+ 1);
748 if (owner_
->lookupChange(i
).type
== Change::DELETED
) {
749 if( ++runparams
.inDeletedInset
== 1)
750 runparams
.changeOfDeletedInset
= owner_
->lookupChange(i
);
753 if (inset
->canTrackChanges()) {
754 column
+= Changes::latexMarkChange(os
, bparams
, running_change
,
755 Change(Change::UNCHANGED
));
756 running_change
= Change(Change::UNCHANGED
);
760 odocstream::pos_type
const len
= os
.tellp();
762 if (inset
->forceLTR()
763 && running_font
.isRightToLeft()
764 // ERT is an exception, it should be output with no decorations at all
765 && inset
->lyxCode() != ERT_CODE
) {
766 if (running_font
.language()->lang() == "farsi")
773 // FIXME: Bug: we can have an empty font change here!
774 // if there has just been a font change, we are going to close it
775 // right now, which means stupid latex code like \textsf{}. AFAIK,
776 // this does not harm dvi output. A minor bug, thus (JMarc)
778 // Some insets cannot be inside a font change command.
779 // However, even such insets *can* be placed in \L or \R
780 // or their equivalents (for RTL language switches), so we don't
781 // close the language in those cases.
782 // ArabTeX, though, cannot handle this special behavior, it seems.
783 bool arabtex
= basefont
.language()->lang() == "arabic_arabtex"
784 || running_font
.language()->lang() == "arabic_arabtex";
785 if (open_font
&& inset
->noFontChange()) {
786 bool closeLanguage
= arabtex
787 || basefont
.isRightToLeft() == running_font
.isRightToLeft();
788 unsigned int count
= running_font
.latexWriteEndChanges(os
,
789 bparams
, runparams
, basefont
, basefont
, closeLanguage
);
791 // if any font properties were closed, update the running_font,
792 // making sure, however, to leave the language as it was
794 // FIXME: probably a better way to keep track of the old
795 // language, than copying the entire font?
796 Font
const copy_font(running_font
);
797 basefont
= owner_
->getLayoutFont(bparams
, outerfont
);
798 running_font
= basefont
;
800 running_font
.setLanguage(copy_font
.language());
801 // leave font open if language is still open
802 open_font
= (running_font
.language() == basefont
.language());
804 runparams
.local_font
= &basefont
;
811 tmp
= inset
->latex(os
, runparams
);
812 } catch (EncodingException
& e
) {
813 // add location information and throw again.
820 if (running_font
.language()->lang() == "farsi")
827 for (int j
= 0; j
< tmp
; ++j
)
830 texrow
.start(owner_
->id(), i
+ 1);
833 column
+= os
.tellp() - len
;
836 if (owner_
->lookupChange(i
).type
== Change::DELETED
)
837 --runparams
.inDeletedInset
;
841 void Paragraph::Private::latexSpecialChar(
843 OutputParams
const & runparams
,
844 Font
const & running_font
,
845 Change
const & running_change
,
846 Layout
const & style
,
848 unsigned int & column
)
850 char_type
const c
= text_
[i
];
852 if (style
.pass_thru
) {
854 // FIXME UNICODE: This can fail if c cannot
855 // be encoded in the current encoding.
860 if (runparams
.verbatim
) {
861 // FIXME UNICODE: This can fail if c cannot
862 // be encoded in the current encoding.
867 // If T1 font encoding is used, use the special
868 // characters it provides.
869 // NOTE: some languages reset the font encoding
871 if (!running_font
.language()->internalFontEncoding()
872 && lyxrc
.fontenc
== "T1" && latexSpecialT1(c
, os
, i
, column
))
875 // \tt font needs special treatment
876 if (running_font
.fontInfo().family() == TYPEWRITER_FAMILY
877 && latexSpecialTypewriter(c
, os
, i
, column
))
880 // Otherwise, we use what LaTeX provides us.
883 os
<< "\\textbackslash{}";
887 os
<< "\\textless{}";
891 os
<< "\\textgreater{}";
902 os
<< "\\char`\\\"{}";
907 case '%': case '#': case '{':
915 os
<< "\\textasciitilde{}";
920 os
<< "\\textasciicircum{}";
927 // avoid being mistaken for optional arguments
935 // Blanks are printed before font switching.
936 // Sure? I am not! (try nice-latex)
937 // I am sure it's correct. LyX might be smarter
938 // in the future, but for now, nothing wrong is
944 if (latexSpecialPhrase(os
, i
, column
, runparams
))
950 Encoding
const & encoding
= *(runparams
.encoding
);
951 if (i
+ 1 < int(text_
.size())) {
952 char_type next
= text_
[i
+ 1];
953 if (Encodings::isCombiningChar(next
)) {
954 column
+= latexSurrogatePair(os
, c
, next
, encoding
) - 1;
960 docstring
const latex
= encoding
.latexChar(c
);
961 if (Encodings::isKnownScriptChar(c
, script
)
962 && prefixIs(latex
, from_ascii("\\" + script
)))
963 column
+= writeScriptChars(os
, latex
,
964 running_change
, encoding
, i
) - 1;
965 else if (latex
.length() > 1 && latex
[latex
.length() - 1] != '}') {
966 // Prevent eating of a following
967 // space or command corruption by
968 // following characters
969 column
+= latex
.length() + 1;
972 column
+= latex
.length() - 1;
980 bool Paragraph::Private::latexSpecialT1(char_type
const c
, odocstream
& os
,
981 pos_type i
, unsigned int & column
)
987 // In T1 encoding, these characters exist
988 // but we should avoid ligatures
989 if (i
+ 1 >= int(text_
.size()) || text_
[i
+ 1] != c
)
991 os
<< "\\textcompwordmark{}";
998 // soul.sty breaks with \char`\"
999 os
<< "\\textquotedbl{}";
1008 bool Paragraph::Private::latexSpecialTypewriter(char_type
const c
, odocstream
& os
,
1009 pos_type i
, unsigned int & column
)
1013 // within \ttfamily, "--" is merged to "-" (no endash)
1014 // so we avoid this rather irritating ligature
1015 if (i
+ 1 < int(text_
.size()) && text_
[i
+ 1] == '-') {
1022 // everything else has to be checked separately
1023 // (depending on the encoding)
1030 bool Paragraph::Private::latexSpecialPhrase(odocstream
& os
, pos_type
& i
,
1031 unsigned int & column
, OutputParams
const & runparams
)
1033 // FIXME: if we have "LaTeX" with a font
1034 // change in the middle (before the 'T', then
1035 // the "TeX" part is still special cased.
1036 // Really we should only operate this on
1037 // "words" for some definition of word
1039 for (size_t pnr
= 0; pnr
< phrases_nr
; ++pnr
) {
1040 if (!isTextAt(special_phrases
[pnr
].phrase
, i
))
1042 if (runparams
.moving_arg
)
1044 os
<< special_phrases
[pnr
].macro
;
1045 i
+= special_phrases
[pnr
].phrase
.length() - 1;
1046 column
+= special_phrases
[pnr
].macro
.length() - 1;
1053 void Paragraph::Private::validate(LaTeXFeatures
& features
,
1054 Layout
const & layout
) const
1056 // check the params.
1057 if (!params_
.spacing().isDefault())
1058 features
.require("setspace");
1061 features
.useLayout(layout
.name());
1064 fontlist_
.validate(features
);
1066 // then the indentation
1067 if (!params_
.leftIndent().zero())
1068 features
.require("ParagraphLeftIndent");
1071 InsetList::const_iterator icit
= insetlist_
.begin();
1072 InsetList::const_iterator iend
= insetlist_
.end();
1073 for (; icit
!= iend
; ++icit
) {
1075 icit
->inset
->validate(features
);
1076 if (layout
.needprotect
&&
1077 icit
->inset
->lyxCode() == FOOT_CODE
)
1078 features
.require("NeedLyXFootnoteCode");
1082 // then the contents
1083 for (pos_type i
= 0; i
< int(text_
.size()) ; ++i
) {
1084 for (size_t pnr
= 0; pnr
< phrases_nr
; ++pnr
) {
1085 if (!special_phrases
[pnr
].builtin
1086 && isTextAt(special_phrases
[pnr
].phrase
, i
)) {
1087 features
.require(special_phrases
[pnr
].phrase
);
1091 Encodings::validate(text_
[i
], features
);
1095 /////////////////////////////////////////////////////////////////////
1099 /////////////////////////////////////////////////////////////////////
1102 Layout
const emptyParagraphLayout
;
1105 Paragraph::Paragraph()
1106 : d(new Paragraph::Private(this, emptyParagraphLayout
))
1113 Paragraph::Paragraph(Paragraph
const & par
)
1114 : itemdepth(par
.itemdepth
),
1115 d(new Paragraph::Private(*par
.d
, this))
1121 Paragraph::Paragraph(Paragraph
const & par
, pos_type beg
, pos_type end
)
1122 : itemdepth(par
.itemdepth
),
1123 d(new Paragraph::Private(*par
.d
, this, beg
, end
))
1129 Paragraph
& Paragraph::operator=(Paragraph
const & par
)
1131 // needed as we will destroy the private part before copying it
1133 itemdepth
= par
.itemdepth
;
1137 d
= new Private(*par
.d
, this);
1144 Paragraph::~Paragraph()
1151 void Paragraph::write(ostream
& os
, BufferParams
const & bparams
,
1152 depth_type
& dth
) const
1154 // The beginning or end of a deeper (i.e. nested) area?
1155 if (dth
!= d
->params_
.depth()) {
1156 if (d
->params_
.depth() > dth
) {
1157 while (d
->params_
.depth() > dth
) {
1158 os
<< "\n\\begin_deeper";
1162 while (d
->params_
.depth() < dth
) {
1163 os
<< "\n\\end_deeper";
1169 // First write the layout
1170 os
<< "\n\\begin_layout " << to_utf8(d
->layout_
->name()) << '\n';
1172 d
->params_
.write(os
);
1174 Font
font1(inherit_font
, bparams
.language
);
1176 Change running_change
= Change(Change::UNCHANGED
);
1179 for (pos_type i
= 0; i
<= size(); ++i
) {
1181 Change change
= lookupChange(i
);
1182 Changes::lyxMarkChange(os
, column
, running_change
, change
);
1183 running_change
= change
;
1188 // Write font changes
1189 Font
const & font2
= getFontSettings(bparams
, i
);
1190 if (font2
!= font1
) {
1191 font2
.lyxWriteChanges(font1
, os
);
1196 char_type
const c
= d
->text_
[i
];
1199 if (Inset
const * inset
= getInset(i
)) {
1200 if (inset
->directWrite()) {
1201 // international char, let it write
1202 // code directly so it's shorter in
1208 os
<< "\\begin_inset ";
1210 os
<< "\n\\end_inset\n\n";
1216 os
<< "\n\\backslash\n";
1220 if (i
+ 1 < size() && d
->text_
[i
+ 1] == ' ') {
1227 if ((column
> 70 && c
== ' ')
1232 // this check is to amend a bug. LyX sometimes
1233 // inserts '\0' this could cause problems.
1235 os
<< to_utf8(docstring(1, c
));
1237 LYXERR0("NUL char in structure.");
1243 os
<< "\n\\end_layout\n";
1247 void Paragraph::validate(LaTeXFeatures
& features
) const
1249 d
->validate(features
, *d
->layout_
);
1253 void Paragraph::insert(pos_type start
, docstring
const & str
,
1254 Font
const & font
, Change
const & change
)
1256 for (size_t i
= 0, n
= str
.size(); i
!= n
; ++i
)
1257 insertChar(start
+ i
, str
[i
], font
, change
);
1261 void Paragraph::appendChar(char_type c
, Font
const & font
,
1262 Change
const & change
)
1265 d
->changes_
.insert(change
, d
->text_
.size());
1266 // when appending characters, no need to update tables
1267 d
->text_
.push_back(c
);
1268 setFont(d
->text_
.size() - 1, font
);
1272 void Paragraph::appendString(docstring
const & s
, Font
const & font
,
1273 Change
const & change
)
1275 pos_type end
= s
.size();
1276 size_t oldsize
= d
->text_
.size();
1277 size_t newsize
= oldsize
+ end
;
1278 size_t capacity
= d
->text_
.capacity();
1279 if (newsize
>= capacity
)
1280 d
->text_
.reserve(max(capacity
+ 100, newsize
));
1282 // when appending characters, no need to update tables
1285 // FIXME: Optimize this!
1286 for (size_t i
= oldsize
; i
!= newsize
; ++i
) {
1288 d
->changes_
.insert(change
, i
);
1290 d
->fontlist_
.set(oldsize
, font
);
1291 d
->fontlist_
.set(newsize
- 1, font
);
1295 void Paragraph::insertChar(pos_type pos
, char_type c
,
1298 d
->insertChar(pos
, c
, Change(trackChanges
?
1299 Change::INSERTED
: Change::UNCHANGED
));
1303 void Paragraph::insertChar(pos_type pos
, char_type c
,
1304 Font
const & font
, bool trackChanges
)
1306 d
->insertChar(pos
, c
, Change(trackChanges
?
1307 Change::INSERTED
: Change::UNCHANGED
));
1312 void Paragraph::insertChar(pos_type pos
, char_type c
,
1313 Font
const & font
, Change
const & change
)
1315 d
->insertChar(pos
, c
, change
);
1320 bool Paragraph::insertInset(pos_type pos
, Inset
* inset
,
1321 Font
const & font
, Change
const & change
)
1323 bool const success
= insertInset(pos
, inset
, change
);
1324 // Set the font/language of the inset...
1330 void Paragraph::resetFonts(Font
const & font
)
1332 d
->fontlist_
.clear();
1333 d
->fontlist_
.set(0, font
);
1334 d
->fontlist_
.set(d
->text_
.size() - 1, font
);
1337 // Gets uninstantiated font setting at position.
1338 Font
const & Paragraph::getFontSettings(BufferParams
const & bparams
,
1342 LYXERR0("pos: " << pos
<< " size: " << size());
1343 LASSERT(pos
<= size(), /**/);
1346 FontList::const_iterator cit
= d
->fontlist_
.fontIterator(pos
);
1347 if (cit
!= d
->fontlist_
.end())
1350 if (pos
== size() && !empty())
1351 return getFontSettings(bparams
, pos
- 1);
1353 // Optimisation: avoid a full font instantiation if there is no
1354 // language change from previous call.
1355 static Font previous_font
;
1356 static Language
const * previous_lang
= 0;
1357 Language
const * lang
= getParLanguage(bparams
);
1358 if (lang
!= previous_lang
) {
1359 previous_lang
= lang
;
1360 previous_font
= Font(inherit_font
, lang
);
1362 return previous_font
;
1366 FontSpan
Paragraph::fontSpan(pos_type pos
) const
1368 LASSERT(pos
<= size(), /**/);
1371 FontList::const_iterator cit
= d
->fontlist_
.begin();
1372 FontList::const_iterator end
= d
->fontlist_
.end();
1373 for (; cit
!= end
; ++cit
) {
1374 if (cit
->pos() >= pos
) {
1375 if (pos
>= beginOfBody())
1376 return FontSpan(max(start
, beginOfBody()),
1379 return FontSpan(start
,
1380 min(beginOfBody() - 1,
1383 start
= cit
->pos() + 1;
1386 // This should not happen, but if so, we take no chances.
1387 // LYXERR0("Paragraph::getEndPosOfFontSpan: This should not happen!");
1388 return FontSpan(pos
, pos
);
1392 // Gets uninstantiated font setting at position 0
1393 Font
const & Paragraph::getFirstFontSettings(BufferParams
const & bparams
) const
1395 if (!empty() && !d
->fontlist_
.empty())
1396 return d
->fontlist_
.begin()->font();
1398 // Optimisation: avoid a full font instantiation if there is no
1399 // language change from previous call.
1400 static Font previous_font
;
1401 static Language
const * previous_lang
= 0;
1402 if (bparams
.language
!= previous_lang
) {
1403 previous_lang
= bparams
.language
;
1404 previous_font
= Font(inherit_font
, bparams
.language
);
1407 return previous_font
;
1411 // Gets the fully instantiated font at a given position in a paragraph
1412 // This is basically the same function as Text::GetFont() in text2.cpp.
1413 // The difference is that this one is used for generating the LaTeX file,
1414 // and thus cosmetic "improvements" are disallowed: This has to deliver
1415 // the true picture of the buffer. (Asger)
1416 Font
const Paragraph::getFont(BufferParams
const & bparams
, pos_type pos
,
1417 Font
const & outerfont
) const
1419 LASSERT(pos
>= 0, /**/);
1421 Font font
= getFontSettings(bparams
, pos
);
1423 pos_type
const body_pos
= beginOfBody();
1424 FontInfo
& fi
= font
.fontInfo();
1426 fi
.realize(d
->layout_
->labelfont
);
1428 fi
.realize(d
->layout_
->font
);
1430 fi
.realize(outerfont
.fontInfo());
1431 fi
.realize(bparams
.getFont().fontInfo());
1437 Font
const Paragraph::getLabelFont
1438 (BufferParams
const & bparams
, Font
const & outerfont
) const
1440 FontInfo tmpfont
= d
->layout_
->labelfont
;
1441 tmpfont
.realize(outerfont
.fontInfo());
1442 tmpfont
.realize(bparams
.getFont().fontInfo());
1443 return Font(tmpfont
, getParLanguage(bparams
));
1447 Font
const Paragraph::getLayoutFont
1448 (BufferParams
const & bparams
, Font
const & outerfont
) const
1450 FontInfo tmpfont
= d
->layout_
->font
;
1451 tmpfont
.realize(outerfont
.fontInfo());
1452 tmpfont
.realize(bparams
.getFont().fontInfo());
1453 return Font(tmpfont
, getParLanguage(bparams
));
1457 /// Returns the height of the highest font in range
1458 FontSize
Paragraph::highestFontInRange
1459 (pos_type startpos
, pos_type endpos
, FontSize def_size
) const
1461 return d
->fontlist_
.highestInRange(startpos
, endpos
, def_size
);
1465 char_type
Paragraph::getUChar(BufferParams
const & bparams
, pos_type pos
) const
1467 char_type c
= d
->text_
[pos
];
1468 if (!lyxrc
.rtl_support
)
1498 if (uc
!= c
&& getFontSettings(bparams
, pos
).isRightToLeft())
1504 void Paragraph::setFont(pos_type pos
, Font
const & font
)
1506 LASSERT(pos
<= size(), /**/);
1508 // First, reduce font against layout/label font
1509 // Update: The setCharFont() routine in text2.cpp already
1510 // reduces font, so we don't need to do that here. (Asger)
1512 d
->fontlist_
.set(pos
, font
);
1516 void Paragraph::makeSameLayout(Paragraph
const & par
)
1518 d
->layout_
= par
.d
->layout_
;
1519 d
->params_
= par
.d
->params_
;
1523 bool Paragraph::stripLeadingSpaces(bool trackChanges
)
1525 if (isFreeSpacing())
1531 while (pos
< size() && (isNewline(pos
) || isLineSeparator(pos
))) {
1532 if (eraseChar(pos
, trackChanges
))
1538 return count
> 0 || pos
> 0;
1542 bool Paragraph::hasSameLayout(Paragraph
const & par
) const
1544 return par
.d
->layout_
== d
->layout_
1545 && d
->params_
.sameLayout(par
.d
->params_
);
1549 depth_type
Paragraph::getDepth() const
1551 return d
->params_
.depth();
1555 depth_type
Paragraph::getMaxDepthAfter() const
1557 if (d
->layout_
->isEnvironment())
1558 return d
->params_
.depth() + 1;
1560 return d
->params_
.depth();
1564 char Paragraph::getAlign() const
1566 if (d
->params_
.align() == LYX_ALIGN_LAYOUT
)
1567 return d
->layout_
->align
;
1569 return d
->params_
.align();
1573 docstring
const & Paragraph::labelString() const
1575 return d
->params_
.labelString();
1579 // the next two functions are for the manual labels
1580 docstring
const Paragraph::getLabelWidthString() const
1582 if (d
->layout_
->margintype
== MARGIN_MANUAL
)
1583 return d
->params_
.labelWidthString();
1585 return _("Senseless with this layout!");
1589 void Paragraph::setLabelWidthString(docstring
const & s
)
1591 d
->params_
.labelWidthString(s
);
1595 docstring
const Paragraph::translateIfPossible(docstring
const & s
,
1596 BufferParams
const & bparams
) const
1598 if (!isAscii(s
) || s
.empty()) {
1599 // This must be a user defined layout. We cannot translate
1600 // this, since gettext accepts only ascii keys.
1603 // Probably standard layout, try to translate
1604 Messages
& m
= getMessages(getParLanguage(bparams
)->code());
1605 return m
.get(to_ascii(s
));
1609 docstring
Paragraph::expandLabel(Layout
const & layout
,
1610 BufferParams
const & bparams
, bool process_appendix
) const
1612 DocumentClass
const & tclass
= bparams
.documentClass();
1615 if (process_appendix
&& d
->params_
.appendix())
1616 fmt
= translateIfPossible(layout
.labelstring_appendix(),
1619 fmt
= translateIfPossible(layout
.labelstring(), bparams
);
1621 if (fmt
.empty() && layout
.labeltype
== LABEL_COUNTER
1622 && !layout
.counter
.empty())
1623 return tclass
.counters().theCounter(layout
.counter
);
1625 // handle 'inherited level parts' in 'fmt',
1626 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
1627 size_t const i
= fmt
.find('@', 0);
1628 if (i
!= docstring::npos
) {
1629 size_t const j
= fmt
.find('@', i
+ 1);
1630 if (j
!= docstring::npos
) {
1631 docstring
parent(fmt
, i
+ 1, j
- i
- 1);
1632 docstring label
= from_ascii("??");
1633 if (tclass
.hasLayout(parent
))
1634 docstring label
= expandLabel(tclass
[parent
], bparams
,
1636 fmt
= docstring(fmt
, 0, i
) + label
1637 + docstring(fmt
, j
+ 1, docstring::npos
);
1641 return tclass
.counters().counterLabel(fmt
);
1645 void Paragraph::applyLayout(Layout
const & new_layout
)
1647 d
->layout_
= &new_layout
;
1648 LyXAlignment
const oldAlign
= d
->params_
.align();
1650 if (!(oldAlign
& d
->layout_
->alignpossible
)) {
1651 frontend::Alert::warning(_("Alignment not permitted"),
1652 _("The new layout does not permit the alignment previously used.\nSetting to default."));
1653 d
->params_
.align(LYX_ALIGN_LAYOUT
);
1658 pos_type
Paragraph::beginOfBody() const
1660 return d
->begin_of_body_
;
1664 void Paragraph::setBeginOfBody()
1666 if (d
->layout_
->labeltype
!= LABEL_MANUAL
) {
1667 d
->begin_of_body_
= 0;
1671 // Unroll the first two cycles of the loop
1672 // and remember the previous character to
1673 // remove unnecessary getChar() calls
1675 pos_type end
= size();
1676 if (i
< end
&& !isNewline(i
)) {
1678 char_type previous_char
= 0;
1681 previous_char
= d
->text_
[i
];
1682 if (!isNewline(i
)) {
1684 while (i
< end
&& previous_char
!= ' ') {
1689 previous_char
= temp
;
1695 d
->begin_of_body_
= i
;
1699 bool Paragraph::forcePlainLayout() const
1701 return inInset().forcePlainLayout();
1705 bool Paragraph::allowParagraphCustomization() const
1707 return inInset().allowParagraphCustomization();
1711 bool Paragraph::usePlainLayout() const
1713 return inInset().usePlainLayout();
1719 // paragraphs inside floats need different alignment tags to avoid
1722 bool noTrivlistCentering(InsetCode code
)
1724 return code
== FLOAT_CODE
1725 || code
== WRAP_CODE
1726 || code
== CELL_CODE
;
1730 string
correction(string
const & orig
)
1732 if (orig
== "flushleft")
1733 return "raggedright";
1734 if (orig
== "flushright")
1735 return "raggedleft";
1736 if (orig
== "center")
1742 string
const corrected_env(string
const & suffix
, string
const & env
,
1743 InsetCode code
, bool const lastpar
)
1745 string output
= suffix
+ "{";
1746 if (noTrivlistCentering(code
)) {
1748 // the last paragraph in non-trivlist-aligned
1749 // context is special (to avoid unwanted whitespace)
1750 if (suffix
== "\\begin")
1751 return "\\" + correction(env
) + "{}";
1754 output
+= correction(env
);
1758 if (suffix
== "\\begin")
1764 void adjust_row_column(string
const & str
, TexRow
& texrow
, int & column
)
1766 if (!contains(str
, "\n"))
1767 column
+= str
.size();
1771 column
= rsplit(str
, tmp
, '\n').size();
1778 int Paragraph::Private::startTeXParParams(BufferParams
const & bparams
,
1779 odocstream
& os
, TexRow
& texrow
,
1780 OutputParams
const & runparams
) const
1784 if (params_
.noindent()) {
1785 os
<< "\\noindent ";
1789 LyXAlignment
const curAlign
= params_
.align();
1791 if (curAlign
== layout_
->align
)
1795 case LYX_ALIGN_NONE
:
1796 case LYX_ALIGN_BLOCK
:
1797 case LYX_ALIGN_LAYOUT
:
1798 case LYX_ALIGN_SPECIAL
:
1800 case LYX_ALIGN_LEFT
:
1801 case LYX_ALIGN_RIGHT
:
1802 case LYX_ALIGN_CENTER
:
1803 if (runparams
.moving_arg
) {
1810 string
const begin_tag
= "\\begin";
1811 InsetCode code
= owner_
->ownerCode();
1812 bool const lastpar
= runparams
.isLastPar
;
1815 case LYX_ALIGN_NONE
:
1816 case LYX_ALIGN_BLOCK
:
1817 case LYX_ALIGN_LAYOUT
:
1818 case LYX_ALIGN_SPECIAL
:
1820 case LYX_ALIGN_LEFT
: {
1822 if (owner_
->getParLanguage(bparams
)->babel() != "hebrew")
1823 output
= corrected_env(begin_tag
, "flushleft", code
, lastpar
);
1825 output
= corrected_env(begin_tag
, "flushright", code
, lastpar
);
1826 os
<< from_ascii(output
);
1827 adjust_row_column(output
, texrow
, column
);
1829 } case LYX_ALIGN_RIGHT
: {
1831 if (owner_
->getParLanguage(bparams
)->babel() != "hebrew")
1832 output
= corrected_env(begin_tag
, "flushright", code
, lastpar
);
1834 output
= corrected_env(begin_tag
, "flushleft", code
, lastpar
);
1835 os
<< from_ascii(output
);
1836 adjust_row_column(output
, texrow
, column
);
1838 } case LYX_ALIGN_CENTER
: {
1840 output
= corrected_env(begin_tag
, "center", code
, lastpar
);
1841 os
<< from_ascii(output
);
1842 adjust_row_column(output
, texrow
, column
);
1851 int Paragraph::Private::endTeXParParams(BufferParams
const & bparams
,
1852 odocstream
& os
, TexRow
& texrow
,
1853 OutputParams
const & runparams
) const
1857 switch (params_
.align()) {
1858 case LYX_ALIGN_NONE
:
1859 case LYX_ALIGN_BLOCK
:
1860 case LYX_ALIGN_LAYOUT
:
1861 case LYX_ALIGN_SPECIAL
:
1863 case LYX_ALIGN_LEFT
:
1864 case LYX_ALIGN_RIGHT
:
1865 case LYX_ALIGN_CENTER
:
1866 if (runparams
.moving_arg
) {
1873 string
const end_tag
= "\n\\par\\end";
1874 InsetCode code
= owner_
->ownerCode();
1875 bool const lastpar
= runparams
.isLastPar
;
1877 switch (params_
.align()) {
1878 case LYX_ALIGN_NONE
:
1879 case LYX_ALIGN_BLOCK
:
1880 case LYX_ALIGN_LAYOUT
:
1881 case LYX_ALIGN_SPECIAL
:
1883 case LYX_ALIGN_LEFT
: {
1885 if (owner_
->getParLanguage(bparams
)->babel() != "hebrew")
1886 output
= corrected_env(end_tag
, "flushleft", code
, lastpar
);
1888 output
= corrected_env(end_tag
, "flushright", code
, lastpar
);
1889 os
<< from_ascii(output
);
1890 adjust_row_column(output
, texrow
, column
);
1892 } case LYX_ALIGN_RIGHT
: {
1894 if (owner_
->getParLanguage(bparams
)->babel() != "hebrew")
1895 output
= corrected_env(end_tag
, "flushright", code
, lastpar
);
1897 output
= corrected_env(end_tag
, "flushleft", code
, lastpar
);
1898 os
<< from_ascii(output
);
1899 adjust_row_column(output
, texrow
, column
);
1901 } case LYX_ALIGN_CENTER
: {
1903 output
= corrected_env(end_tag
, "center", code
, lastpar
);
1904 os
<< from_ascii(output
);
1905 adjust_row_column(output
, texrow
, column
);
1914 // This one spits out the text of the paragraph
1915 bool Paragraph::latex(BufferParams
const & bparams
,
1916 Font
const & outerfont
,
1917 odocstream
& os
, TexRow
& texrow
,
1918 OutputParams
const & runparams
,
1919 int start_pos
, int end_pos
) const
1921 LYXERR(Debug::LATEX
, "Paragraph::latex... " << this);
1923 bool return_value
= false;
1925 bool const allowcust
= allowParagraphCustomization();
1927 // FIXME This check should not be needed. Perhaps issue an
1928 // error if it triggers.
1929 Layout
const & style
= forcePlainLayout() ?
1930 bparams
.documentClass().plainLayout() : *d
->layout_
;
1932 // Current base font for all inherited font changes, without any
1933 // change caused by an individual character, except for the language:
1934 // It is set to the language of the first character.
1935 // As long as we are in the label, this font is the base font of the
1936 // label. Before the first body character it is set to the base font
1940 // Maybe we have to create a optional argument.
1941 pos_type body_pos
= beginOfBody();
1942 unsigned int column
= 0;
1945 // the optional argument is kept in curly brackets in
1946 // case it contains a ']'
1949 basefont
= getLabelFont(bparams
, outerfont
);
1951 basefont
= getLayoutFont(bparams
, outerfont
);
1954 // Which font is currently active?
1955 Font
running_font(basefont
);
1956 // Do we have an open font change?
1957 bool open_font
= false;
1959 Change runningChange
= Change(Change::UNCHANGED
);
1961 texrow
.start(id(), 0);
1963 // if the paragraph is empty, the loop will not be entered at all
1965 if (style
.isCommand()) {
1970 column
+= d
->startTeXParParams(bparams
, os
, texrow
,
1974 for (pos_type i
= 0; i
< size(); ++i
) {
1975 // First char in paragraph or after label?
1976 if (i
== body_pos
) {
1979 column
+= running_font
.latexWriteEndChanges(
1980 os
, bparams
, runparams
,
1981 basefont
, basefont
);
1984 basefont
= getLayoutFont(bparams
, outerfont
);
1985 running_font
= basefont
;
1987 column
+= Changes::latexMarkChange(os
, bparams
,
1988 runningChange
, Change(Change::UNCHANGED
));
1989 runningChange
= Change(Change::UNCHANGED
);
1994 if (style
.isCommand()) {
2000 column
+= d
->startTeXParParams(bparams
, os
,
2005 Change
const & change
= runparams
.inDeletedInset
? runparams
.changeOfDeletedInset
2008 if (bparams
.outputChanges
&& runningChange
!= change
) {
2010 column
+= running_font
.latexWriteEndChanges(
2011 os
, bparams
, runparams
, basefont
, basefont
);
2014 basefont
= getLayoutFont(bparams
, outerfont
);
2015 running_font
= basefont
;
2017 column
+= Changes::latexMarkChange(os
, bparams
, runningChange
, change
);
2018 runningChange
= change
;
2021 // do not output text which is marked deleted
2022 // if change tracking output is disabled
2023 if (!bparams
.outputChanges
&& change
.type
== Change::DELETED
) {
2029 // Fully instantiated font
2030 Font
const font
= getFont(bparams
, i
, outerfont
);
2032 Font
const last_font
= running_font
;
2034 // Do we need to close the previous font?
2036 (font
!= running_font
||
2037 font
.language() != running_font
.language()))
2039 column
+= running_font
.latexWriteEndChanges(
2040 os
, bparams
, runparams
, basefont
,
2041 (i
== body_pos
-1) ? basefont
: font
);
2042 running_font
= basefont
;
2046 // close babel's font environment before opening CJK.
2047 if (!running_font
.language()->babel().empty() &&
2048 font
.language()->encoding()->package() == Encoding::CJK
) {
2049 string end_tag
= subst(lyxrc
.language_command_end
,
2051 running_font
.language()->babel());
2052 os
<< from_ascii(end_tag
);
2053 column
+= end_tag
.length();
2056 // Switch file encoding if necessary (and allowed)
2057 if (!runparams
.verbatim
&&
2058 runparams
.encoding
->package() != Encoding::none
&&
2059 font
.language()->encoding()->package() != Encoding::none
) {
2060 pair
<bool, int> const enc_switch
= switchEncoding(os
, bparams
,
2061 runparams
, *(font
.language()->encoding()));
2062 if (enc_switch
.first
) {
2063 column
+= enc_switch
.second
;
2064 runparams
.encoding
= font
.language()->encoding();
2068 char_type
const c
= d
->text_
[i
];
2070 // Do we need to change font?
2071 if ((font
!= running_font
||
2072 font
.language() != running_font
.language()) &&
2075 odocstringstream ods
;
2076 column
+= font
.latexWriteStartChanges(ods
, bparams
,
2077 runparams
, basefont
,
2079 running_font
= font
;
2081 docstring fontchange
= ods
.str();
2082 // check if the fontchange ends with a trailing blank
2083 // (like "\small " (see bug 3382)
2084 if (suffixIs(fontchange
, ' ') && c
== ' ')
2085 os
<< fontchange
.substr(0, fontchange
.size() - 1)
2086 << from_ascii("{}");
2091 // FIXME: think about end_pos implementation...
2092 if (c
== ' ' && i
>= start_pos
&& (end_pos
== -1 || i
< end_pos
)) {
2093 // FIXME: integrate this case in latexSpecialChar
2094 // Do not print the separation of the optional argument
2095 // if style.pass_thru is false. This works because
2096 // latexSpecialChar ignores spaces if
2097 // style.pass_thru is false.
2098 if (i
!= body_pos
- 1) {
2099 if (d
->simpleTeXBlanks(
2100 runparams
, os
, texrow
,
2101 i
, column
, font
, style
)) {
2102 // A surrogate pair was output. We
2103 // must not call latexSpecialChar
2104 // in this iteration, since it would output
2105 // the combining character again.
2112 OutputParams rp
= runparams
;
2113 rp
.free_spacing
= style
.free_spacing
;
2114 rp
.local_font
= &font
;
2115 rp
.intitle
= style
.intitle
;
2117 // Two major modes: LaTeX or plain
2118 // Handle here those cases common to both modes
2119 // and then split to handle the two modes separately.
2120 if (c
== META_INSET
) {
2121 if (i
>= start_pos
&& (end_pos
== -1 || i
< end_pos
)) {
2122 d
->latexInset(bparams
, os
,
2123 texrow
, rp
, running_font
,
2124 basefont
, outerfont
, open_font
,
2125 runningChange
, style
, i
, column
);
2128 if (i
>= start_pos
&& (end_pos
== -1 || i
< end_pos
)) {
2130 d
->latexSpecialChar(os
, rp
, running_font
, runningChange
,
2132 } catch (EncodingException
& e
) {
2133 if (runparams
.dryrun
) {
2134 os
<< "<" << _("LyX Warning: ")
2135 << _("uncodable character") << " '";
2139 // add location information and throw again.
2148 // Set the encoding to that returned from latexSpecialChar (see
2149 // comment for encoding member in OutputParams.h)
2150 runparams
.encoding
= rp
.encoding
;
2153 // If we have an open font definition, we have to close it
2155 #ifdef FIXED_LANGUAGE_END_DETECTION
2158 .latexWriteEndChanges(os
, bparams
, runparams
,
2160 next_
->getFont(bparams
, 0, outerfont
));
2162 running_font
.latexWriteEndChanges(os
, bparams
,
2163 runparams
, basefont
, basefont
);
2166 //FIXME: For now we ALWAYS have to close the foreign font settings if they are
2167 //FIXME: there as we start another \selectlanguage with the next paragraph if
2168 //FIXME: we are in need of this. This should be fixed sometime (Jug)
2169 running_font
.latexWriteEndChanges(os
, bparams
, runparams
,
2170 basefont
, basefont
);
2174 column
+= Changes::latexMarkChange(os
, bparams
, runningChange
, Change(Change::UNCHANGED
));
2176 // Needed if there is an optional argument but no contents.
2177 if (body_pos
> 0 && body_pos
== size()) {
2179 return_value
= false;
2183 column
+= d
->endTeXParParams(bparams
, os
, texrow
,
2187 LYXERR(Debug::LATEX
, "Paragraph::latex... done " << this);
2188 return return_value
;
2192 bool Paragraph::emptyTag() const
2194 for (pos_type i
= 0; i
< size(); ++i
) {
2195 if (Inset
const * inset
= getInset(i
)) {
2196 InsetCode lyx_code
= inset
->lyxCode();
2197 if (lyx_code
!= TOC_CODE
&&
2198 lyx_code
!= INCLUDE_CODE
&&
2199 lyx_code
!= GRAPHICS_CODE
&&
2200 lyx_code
!= ERT_CODE
&&
2201 lyx_code
!= LISTINGS_CODE
&&
2202 lyx_code
!= FLOAT_CODE
&&
2203 lyx_code
!= TABULAR_CODE
) {
2207 char_type c
= d
->text_
[i
];
2208 if (c
!= ' ' && c
!= '\t')
2216 string
Paragraph::getID(Buffer
const & buf
, OutputParams
const & runparams
)
2219 for (pos_type i
= 0; i
< size(); ++i
) {
2220 if (Inset
const * inset
= getInset(i
)) {
2221 InsetCode lyx_code
= inset
->lyxCode();
2222 if (lyx_code
== LABEL_CODE
) {
2223 InsetLabel
const * const il
= static_cast<InsetLabel
const *>(inset
);
2224 docstring
const & id
= il
->getParam("name");
2225 return "id='" + to_utf8(sgml::cleanID(buf
, runparams
, id
)) + "'";
2233 pos_type
Paragraph::firstWord(odocstream
& os
, OutputParams
const & runparams
)
2237 for (i
= 0; i
< size(); ++i
) {
2238 if (Inset
const * inset
= getInset(i
)) {
2239 inset
->docbook(os
, runparams
);
2241 char_type c
= d
->text_
[i
];
2244 os
<< sgml::escapeChar(c
);
2251 bool Paragraph::Private::onlyText(Buffer
const & buf
, Font
const & outerfont
, pos_type initial
) const
2254 pos_type size
= text_
.size();
2255 for (pos_type i
= initial
; i
< size
; ++i
) {
2256 Font font
= owner_
->getFont(buf
.params(), i
, outerfont
);
2257 if (text_
[i
] == META_INSET
)
2259 if (i
!= initial
&& font
!= font_old
)
2268 void Paragraph::simpleDocBookOnePar(Buffer
const & buf
,
2270 OutputParams
const & runparams
,
2271 Font
const & outerfont
,
2272 pos_type initial
) const
2274 bool emph_flag
= false;
2276 Layout
const & style
= *d
->layout_
;
2278 style
.labeltype
== LABEL_MANUAL
? style
.labelfont
: style
.font
;
2280 if (style
.pass_thru
&& !d
->onlyText(buf
, outerfont
, initial
))
2283 // parsing main loop
2284 for (pos_type i
= initial
; i
< size(); ++i
) {
2285 Font font
= getFont(buf
.params(), i
, outerfont
);
2287 // handle <emphasis> tag
2288 if (font_old
.emph() != font
.fontInfo().emph()) {
2289 if (font
.fontInfo().emph() == FONT_ON
) {
2292 } else if (i
!= initial
) {
2293 os
<< "</emphasis>";
2298 if (Inset
const * inset
= getInset(i
)) {
2299 inset
->docbook(os
, runparams
);
2301 char_type c
= d
->text_
[i
];
2303 if (style
.pass_thru
)
2306 os
<< sgml::escapeChar(c
);
2308 font_old
= font
.fontInfo();
2312 os
<< "</emphasis>";
2315 if (style
.free_spacing
)
2317 if (style
.pass_thru
&& !d
->onlyText(buf
, outerfont
, initial
))
2322 bool Paragraph::isHfill(pos_type pos
) const
2324 Inset
const * inset
= getInset(pos
);
2325 return inset
&& (inset
->lyxCode() == SPACE_CODE
&&
2326 inset
->isStretchableSpace());
2330 bool Paragraph::isNewline(pos_type pos
) const
2332 Inset
const * inset
= getInset(pos
);
2333 return inset
&& inset
->lyxCode() == NEWLINE_CODE
;
2337 bool Paragraph::isLineSeparator(pos_type pos
) const
2339 char_type
const c
= d
->text_
[pos
];
2340 if (isLineSeparatorChar(c
))
2342 Inset
const * inset
= getInset(pos
);
2343 return inset
&& inset
->isLineSeparator();
2347 /// Used by the spellchecker
2348 bool Paragraph::isLetter(pos_type pos
) const
2350 if (Inset
const * inset
= getInset(pos
))
2351 return inset
->isLetter();
2352 char_type
const c
= d
->text_
[pos
];
2353 return isLetterChar(c
) || isDigit(c
);
2357 bool Paragraph::isChar(pos_type pos
) const
2359 if (Inset
const * inset
= getInset(pos
))
2360 return inset
->isChar();
2361 char_type
const c
= d
->text_
[pos
];
2362 return !isLetterChar(c
) && !isDigit(c
) && !lyx::isSpace(c
);
2366 bool Paragraph::isSpace(pos_type pos
) const
2368 if (Inset
const * inset
= getInset(pos
))
2369 return inset
->isSpace();
2370 char_type
const c
= d
->text_
[pos
];
2371 return lyx::isSpace(c
);
2376 Paragraph::getParLanguage(BufferParams
const & bparams
) const
2379 return getFirstFontSettings(bparams
).language();
2380 // FIXME: we should check the prev par as well (Lgb)
2381 return bparams
.language
;
2385 bool Paragraph::isRTL(BufferParams
const & bparams
) const
2387 return lyxrc
.rtl_support
2388 && getParLanguage(bparams
)->rightToLeft()
2389 && ownerCode() != ERT_CODE
2390 && ownerCode() != LISTINGS_CODE
;
2394 void Paragraph::changeLanguage(BufferParams
const & bparams
,
2395 Language
const * from
, Language
const * to
)
2397 // change language including dummy font change at the end
2398 for (pos_type i
= 0; i
<= size(); ++i
) {
2399 Font font
= getFontSettings(bparams
, i
);
2400 if (font
.language() == from
) {
2401 font
.setLanguage(to
);
2408 bool Paragraph::isMultiLingual(BufferParams
const & bparams
) const
2410 Language
const * doc_language
= bparams
.language
;
2411 FontList::const_iterator cit
= d
->fontlist_
.begin();
2412 FontList::const_iterator end
= d
->fontlist_
.end();
2414 for (; cit
!= end
; ++cit
)
2415 if (cit
->font().language() != ignore_language
&&
2416 cit
->font().language() != latex_language
&&
2417 cit
->font().language() != doc_language
)
2423 docstring
Paragraph::asString(int options
) const
2425 return asString(0, size(), options
);
2429 docstring
Paragraph::asString(pos_type beg
, pos_type end
, int options
) const
2431 odocstringstream os
;
2434 && options
& AS_STR_LABEL
2435 && !d
->params_
.labelString().empty())
2436 os
<< d
->params_
.labelString() << ' ';
2438 for (pos_type i
= beg
; i
< end
; ++i
) {
2439 char_type
const c
= d
->text_
[i
];
2440 if (isPrintable(c
) || c
== '\t'
2441 || (c
== '\n' && options
& AS_STR_NEWLINES
))
2443 else if (c
== META_INSET
&& options
& AS_STR_INSETS
)
2444 getInset(i
)->tocString(os
);
2451 docstring
Paragraph::stringify(pos_type beg
, pos_type end
, int options
, OutputParams
& runparams
) const
2453 odocstringstream os
;
2456 && options
& AS_STR_LABEL
2457 && !d
->params_
.labelString().empty())
2458 os
<< d
->params_
.labelString() << ' ';
2460 for (pos_type i
= beg
; i
< end
; ++i
) {
2461 char_type
const c
= d
->text_
[i
];
2462 if (isPrintable(c
) || c
== '\t'
2463 || (c
== '\n' && options
& AS_STR_NEWLINES
))
2465 else if (c
== META_INSET
&& options
& AS_STR_INSETS
) {
2466 getInset(i
)->plaintext(os
, runparams
);
2474 void Paragraph::setInsetOwner(Inset
const * inset
)
2476 d
->inset_owner_
= inset
;
2480 int Paragraph::id() const
2486 Layout
const & Paragraph::layout() const
2492 void Paragraph::setLayout(Layout
const & layout
)
2494 d
->layout_
= &layout
;
2498 void Paragraph::setDefaultLayout(DocumentClass
const & tc
)
2500 setLayout(tc
.defaultLayout());
2504 void Paragraph::setPlainLayout(DocumentClass
const & tc
)
2506 setLayout(tc
.plainLayout());
2510 void Paragraph::setPlainOrDefaultLayout(DocumentClass
const & tclass
)
2512 if (usePlainLayout())
2513 setPlainLayout(tclass
);
2515 setDefaultLayout(tclass
);
2519 Inset
const & Paragraph::inInset() const
2521 LASSERT(d
->inset_owner_
, throw ExceptionMessage(BufferException
,
2522 _("Memory problem"), _("Paragraph not properly initialized")));
2523 return *d
->inset_owner_
;
2527 InsetCode
Paragraph::ownerCode() const
2529 return d
->inset_owner_
? d
->inset_owner_
->lyxCode() : NO_CODE
;
2533 ParagraphParameters
& Paragraph::params()
2539 ParagraphParameters
const & Paragraph::params() const
2545 bool Paragraph::isFreeSpacing() const
2547 if (d
->layout_
->free_spacing
)
2549 return d
->inset_owner_
&& d
->inset_owner_
->isFreeSpacing();
2553 bool Paragraph::allowEmpty() const
2555 if (d
->layout_
->keepempty
)
2557 return d
->inset_owner_
&& d
->inset_owner_
->allowEmpty();
2561 char_type
Paragraph::transformChar(char_type c
, pos_type pos
) const
2563 if (!Encodings::isArabicChar(c
))
2566 char_type prev_char
= ' ';
2567 char_type next_char
= ' ';
2569 for (pos_type i
= pos
- 1; i
>= 0; --i
) {
2570 char_type
const par_char
= d
->text_
[i
];
2571 if (!Encodings::isArabicComposeChar(par_char
)) {
2572 prev_char
= par_char
;
2577 for (pos_type i
= pos
+ 1, end
= size(); i
< end
; ++i
) {
2578 char_type
const par_char
= d
->text_
[i
];
2579 if (!Encodings::isArabicComposeChar(par_char
)) {
2580 next_char
= par_char
;
2585 if (Encodings::isArabicChar(next_char
)) {
2586 if (Encodings::isArabicChar(prev_char
) &&
2587 !Encodings::isArabicSpecialChar(prev_char
))
2588 return Encodings::transformChar(c
, Encodings::FORM_MEDIAL
);
2590 return Encodings::transformChar(c
, Encodings::FORM_INITIAL
);
2592 if (Encodings::isArabicChar(prev_char
) &&
2593 !Encodings::isArabicSpecialChar(prev_char
))
2594 return Encodings::transformChar(c
, Encodings::FORM_FINAL
);
2596 return Encodings::transformChar(c
, Encodings::FORM_ISOLATED
);
2601 int Paragraph::checkBiblio(Buffer
const & buffer
)
2604 // This is getting more and more a mess. ...We really should clean
2605 // up this bibitem issue for 1.6. See also bug 2743.
2607 // Add bibitem insets if necessary
2608 if (d
->layout_
->labeltype
!= LABEL_BIBLIO
)
2611 bool hasbibitem
= !d
->insetlist_
.empty()
2612 // Insist on it being in pos 0
2613 && d
->text_
[0] == META_INSET
2614 && d
->insetlist_
.begin()->inset
->lyxCode() == BIBITEM_CODE
;
2616 bool track_changes
= buffer
.params().trackChanges
;
2621 // remove a bibitem in pos != 0
2622 // restore it later in pos 0 if necessary
2623 // (e.g. if a user inserts contents _before_ the item)
2624 // we're assuming there's only one of these, which there
2626 int erasedInsetPosition
= -1;
2627 InsetList::iterator it
= d
->insetlist_
.begin();
2628 InsetList::iterator end
= d
->insetlist_
.end();
2629 for (; it
!= end
; ++it
)
2630 if (it
->inset
->lyxCode() == BIBITEM_CODE
2632 InsetBibitem
* olditem
= static_cast<InsetBibitem
*>(it
->inset
);
2633 oldkey
= olditem
->getParam("key");
2634 oldlabel
= olditem
->getParam("label");
2635 erasedInsetPosition
= it
->pos
;
2636 eraseChar(erasedInsetPosition
, track_changes
);
2640 // There was an InsetBibitem at the beginning, and we didn't
2641 // have to erase one.
2642 if (hasbibitem
&& erasedInsetPosition
< 0)
2645 // There was an InsetBibitem at the beginning and we did have to
2646 // erase one. So we give its properties to the beginning inset.
2648 InsetBibitem
* inset
=
2649 static_cast<InsetBibitem
*>(d
->insetlist_
.begin()->inset
);
2650 if (!oldkey
.empty())
2651 inset
->setParam("key", oldkey
);
2652 inset
->setParam("label", oldlabel
);
2653 return -erasedInsetPosition
;
2656 // There was no inset at the beginning, so we need to create one with
2657 // the key and label of the one we erased.
2658 InsetBibitem
* inset
=
2659 new InsetBibitem(buffer
, InsetCommandParams(BIBITEM_CODE
));
2660 // restore values of previously deleted item in this par.
2661 if (!oldkey
.empty())
2662 inset
->setParam("key", oldkey
);
2663 inset
->setParam("label", oldlabel
);
2664 insertInset(0, static_cast<Inset
*>(inset
),
2665 Change(track_changes
? Change::INSERTED
: Change::UNCHANGED
));
2671 void Paragraph::checkAuthors(AuthorList
const & authorList
)
2673 d
->changes_
.checkAuthors(authorList
);
2677 bool Paragraph::isUnchanged(pos_type pos
) const
2679 return lookupChange(pos
).type
== Change::UNCHANGED
;
2683 bool Paragraph::isInserted(pos_type pos
) const
2685 return lookupChange(pos
).type
== Change::INSERTED
;
2689 bool Paragraph::isDeleted(pos_type pos
) const
2691 return lookupChange(pos
).type
== Change::DELETED
;
2695 InsetList
const & Paragraph::insetList() const
2697 return d
->insetlist_
;
2701 void Paragraph::setBuffer(Buffer
& b
)
2703 d
->insetlist_
.setBuffer(b
);
2707 Inset
* Paragraph::releaseInset(pos_type pos
)
2709 Inset
* inset
= d
->insetlist_
.release(pos
);
2710 /// does not honour change tracking!
2711 eraseChar(pos
, false);
2716 Inset
* Paragraph::getInset(pos_type pos
)
2718 return (pos
< pos_type(d
->text_
.size()) && d
->text_
[pos
] == META_INSET
)
2719 ? d
->insetlist_
.get(pos
) : 0;
2723 Inset
const * Paragraph::getInset(pos_type pos
) const
2725 return (pos
< pos_type(d
->text_
.size()) && d
->text_
[pos
] == META_INSET
)
2726 ? d
->insetlist_
.get(pos
) : 0;
2730 void Paragraph::changeCase(BufferParams
const & bparams
, pos_type pos
,
2731 pos_type
& right
, TextCase action
)
2733 // process sequences of modified characters; in change
2734 // tracking mode, this approach results in much better
2735 // usability than changing case on a char-by-char basis
2738 bool const trackChanges
= bparams
.trackChanges
;
2740 bool capitalize
= true;
2742 for (; pos
< right
; ++pos
) {
2743 char_type oldChar
= d
->text_
[pos
];
2744 char_type newChar
= oldChar
;
2746 // ignore insets and don't play with deleted text!
2747 if (oldChar
!= META_INSET
&& !isDeleted(pos
)) {
2749 case text_lowercase
:
2750 newChar
= lowercase(oldChar
);
2752 case text_capitalization
:
2754 newChar
= uppercase(oldChar
);
2758 case text_uppercase
:
2759 newChar
= uppercase(oldChar
);
2764 if (!isLetter(pos
) || isDeleted(pos
)) {
2765 // permit capitalization again
2769 if (oldChar
!= newChar
)
2772 if (oldChar
== newChar
|| pos
== right
- 1) {
2773 if (oldChar
!= newChar
) {
2774 // step behind the changing area
2777 int erasePos
= pos
- changes
.size();
2778 for (size_t i
= 0; i
< changes
.size(); i
++) {
2779 insertChar(pos
, changes
[i
],
2780 getFontSettings(bparams
,
2783 if (!eraseChar(erasePos
, trackChanges
)) {
2786 ++right
; // expand selection
2795 bool Paragraph::find(docstring
const & str
, bool cs
, bool mw
,
2796 pos_type pos
, bool del
) const
2798 int const strsize
= str
.length();
2800 pos_type
const parsize
= d
->text_
.size();
2801 for (i
= 0; pos
+ i
< parsize
; ++i
) {
2804 if (cs
&& str
[i
] != d
->text_
[pos
+ i
])
2806 if (!cs
&& uppercase(str
[i
]) != uppercase(d
->text_
[pos
+ i
]))
2808 if (!del
&& isDeleted(pos
+ i
))
2815 // if necessary, check whether string matches word
2817 if (pos
> 0 && isLetter(pos
- 1))
2819 if (pos
+ strsize
< parsize
2820 && isLetter(pos
+ strsize
))
2828 char_type
Paragraph::getChar(pos_type pos
) const
2830 return d
->text_
[pos
];
2834 pos_type
Paragraph::size() const
2836 return d
->text_
.size();
2840 bool Paragraph::empty() const
2842 return d
->text_
.empty();
2846 bool Paragraph::isInset(pos_type pos
) const
2848 return d
->text_
[pos
] == META_INSET
;
2852 bool Paragraph::isSeparator(pos_type pos
) const
2854 //FIXME: Are we sure this can be the only separator?
2855 return d
->text_
[pos
] == ' ';
2859 void Paragraph::deregisterWords()
2861 Private::Words::const_iterator it
;
2862 WordList
& wl
= theWordList();
2863 for (it
= d
->words_
.begin(); it
!= d
->words_
.end(); ++it
)
2869 void Paragraph::collectWords(CursorSlice
const & sl
)
2872 bool inword
= false;
2874 //lyxerr << "Words: ";
2875 pos_type n
= size();
2876 for (pos_type pos
= 0; pos
!= n
; ++pos
) {
2880 if (!isLetter(pos
)) {
2889 CursorSlice from
= sl
;
2890 CursorSlice to
= sl
;
2893 from
.text()->getWord(from
, to
, WHOLE_WORD
);
2894 if (to
.pos() - from
.pos() < 6)
2896 docstring word
= asString(from
.pos(), to
.pos(), false);
2897 d
->words_
.insert(word
);
2898 //lyxerr << word << " ";
2900 //lyxerr << std::endl;
2904 void Paragraph::registerWords()
2906 Private::Words::const_iterator it
;
2907 WordList
& wl
= theWordList();
2908 for (it
= d
->words_
.begin(); it
!= d
->words_
.end(); ++it
)
2913 void Paragraph::updateWords(CursorSlice
const & sl
)
2915 LASSERT(&sl
.paragraph() == this, /**/);