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"
23 #include "BufferParams.h"
29 #include "InsetList.h"
31 #include "LaTeXFeatures.h"
39 #include "OutputParams.h"
40 #include "output_latex.h"
41 #include "paragraph_funcs.h"
42 #include "ParagraphParameters.h"
47 #include "frontends/alert.h"
48 #include "frontends/FontMetrics.h"
50 #include "insets/InsetBibitem.h"
51 #include "insets/InsetOptArg.h"
53 #include "support/lstrings.h"
54 #include "support/textutils.h"
55 #include "support/convert.h"
56 #include "support/unicode.h"
66 using support::contains
;
67 using support::prefixIs
;
68 using support::suffixIs
;
69 using support::rsplit
;
73 /////////////////////////////////////////////////////////////////////
77 /////////////////////////////////////////////////////////////////////
79 class Paragraph::Private
83 Private(Paragraph
* owner
);
84 /// "Copy constructor"
85 Private(Private
const &, Paragraph
* owner
);
88 value_type
getChar(pos_type pos
) const;
90 void insertChar(pos_type pos
, value_type c
, Change
const & change
);
92 /// Output the surrogate pair formed by \p c and \p next to \p os.
93 /// \return the number of characters written.
94 int latexSurrogatePair(odocstream
& os
, value_type c
, value_type next
,
97 /// Output a space in appropriate formatting (or a surrogate pair
98 /// if the next character is a combining character).
99 /// \return whether a surrogate pair was output.
100 bool simpleTeXBlanks(Encoding
const &,
101 odocstream
&, TexRow
& texrow
,
103 unsigned int & column
,
105 Layout
const & style
);
107 /// Output consecutive known unicode chars, belonging to the same
108 /// language as specified by \p preamble, to \p os starting from \p c.
109 /// \return the number of characters written.
110 int knownLangChars(odocstream
& os
, value_type c
, string
& preamble
,
111 Change
&, Encoding
const &, pos_type
&);
113 void latexInset(Buffer
const &, BufferParams
const &,
115 TexRow
& texrow
, OutputParams
&,
118 Font
const & outerfont
,
120 Change
& running_change
,
121 Layout
const & style
,
123 unsigned int & column
);
126 void latexSpecialChar(
128 OutputParams
& runparams
,
130 Change
& running_change
,
131 Layout
const & style
,
133 unsigned int & column
);
136 void validate(LaTeXFeatures
& features
,
137 Layout
const & layout
) const;
140 pos_type
size() const { return owner_
->size(); }
142 /// match a string against a particular point in the paragraph
143 bool isTextAt(std::string
const & str
, pos_type pos
) const;
145 /// Which Paragraph owns us?
149 Inset
* inset_owner_
;
157 static unsigned int paragraph_id
;
159 ParagraphParameters params_
;
161 /// for recording and looking up changes
165 InsetList insetlist_
;
172 using std::upper_bound
;
173 using std::lower_bound
;
177 // Initialization of the counter for the paragraph id's,
178 unsigned int Paragraph::Private::paragraph_id
= 0;
182 struct special_phrase
{
188 special_phrase
const special_phrases
[] = {
189 { "LyX", from_ascii("\\protect\\LyX{}"), false },
190 { "TeX", from_ascii("\\protect\\TeX{}"), true },
191 { "LaTeX2e", from_ascii("\\protect\\LaTeXe{}"), true },
192 { "LaTeX", from_ascii("\\protect\\LaTeX{}"), true },
195 size_t const phrases_nr
= sizeof(special_phrases
)/sizeof(special_phrase
);
200 Paragraph::Private::Private(Paragraph
* owner
)
204 id_
= paragraph_id
++;
208 Paragraph::Private::Private(Private
const & p
, Paragraph
* owner
)
209 : owner_(owner
), inset_owner_(p
.inset_owner_
), fontlist_(p
.fontlist_
),
210 params_(p
.params_
), changes_(p
.changes_
), insetlist_(p
.insetlist_
)
212 id_
= paragraph_id
++;
216 bool Paragraph::isChanged(pos_type start
, pos_type end
) const
218 BOOST_ASSERT(start
>= 0 && start
<= size());
219 BOOST_ASSERT(end
> start
&& end
<= size() + 1);
221 return d
->changes_
.isChanged(start
, end
);
225 bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges
) const {
226 // keep the logic here in sync with the logic of eraseChars()
232 Change change
= d
->changes_
.lookup(size());
234 return change
.type
== Change::INSERTED
&& change
.author
== 0;
238 void Paragraph::setChange(Change
const & change
)
240 // beware of the imaginary end-of-par character!
241 d
->changes_
.set(change
, 0, size() + 1);
244 * Propagate the change recursively - but not in case of DELETED!
246 * Imagine that your co-author makes changes in an existing inset. He
247 * sends your document to you and you come to the conclusion that the
248 * inset should go completely. If you erase it, LyX must not delete all
249 * text within the inset. Otherwise, the change tracked insertions of
250 * your co-author get lost and there is no way to restore them later.
252 * Conclusion: An inset's content should remain untouched if you delete it
255 if (change
.type
!= Change::DELETED
) {
256 for (pos_type pos
= 0; pos
< size(); ++pos
) {
258 getInset(pos
)->setChange(change
);
264 void Paragraph::setChange(pos_type pos
, Change
const & change
)
266 BOOST_ASSERT(pos
>= 0 && pos
<= size());
268 d
->changes_
.set(change
, pos
);
270 // see comment in setChange(Change const &) above
272 if (change
.type
!= Change::DELETED
&&
273 pos
< size() && isInset(pos
)) {
274 getInset(pos
)->setChange(change
);
279 Change
const & Paragraph::lookupChange(pos_type pos
) const
281 BOOST_ASSERT(pos
>= 0 && pos
<= size());
283 return d
->changes_
.lookup(pos
);
287 void Paragraph::acceptChanges(BufferParams
const & bparams
, pos_type start
,
290 BOOST_ASSERT(start
>= 0 && start
<= size());
291 BOOST_ASSERT(end
> start
&& end
<= size() + 1);
293 for (pos_type pos
= start
; pos
< end
; ++pos
) {
294 switch (lookupChange(pos
).type
) {
295 case Change::UNCHANGED
:
296 // accept changes in nested inset
297 if (pos
< size() && isInset(pos
))
298 getInset(pos
)->acceptChanges(bparams
);
302 case Change::INSERTED
:
303 d
->changes_
.set(Change(Change::UNCHANGED
), pos
);
304 // also accept changes in nested inset
305 if (pos
< size() && isInset(pos
)) {
306 getInset(pos
)->acceptChanges(bparams
);
310 case Change::DELETED
:
311 // Suppress access to non-existent
312 // "end-of-paragraph char"
314 eraseChar(pos
, false);
325 void Paragraph::rejectChanges(BufferParams
const & bparams
,
326 pos_type start
, pos_type end
)
328 BOOST_ASSERT(start
>= 0 && start
<= size());
329 BOOST_ASSERT(end
> start
&& end
<= size() + 1);
331 for (pos_type pos
= start
; pos
< end
; ++pos
) {
332 switch (lookupChange(pos
).type
) {
333 case Change::UNCHANGED
:
334 // reject changes in nested inset
335 if (pos
< size() && isInset(pos
)) {
336 getInset(pos
)->rejectChanges(bparams
);
340 case Change::INSERTED
:
341 // Suppress access to non-existent
342 // "end-of-paragraph char"
344 eraseChar(pos
, false);
350 case Change::DELETED
:
351 d
->changes_
.set(Change(Change::UNCHANGED
), pos
);
353 // Do NOT reject changes within a deleted inset!
354 // There may be insertions of a co-author inside of it!
362 Paragraph::value_type
Paragraph::Private::getChar(pos_type pos
) const
364 BOOST_ASSERT(pos
>= 0 && pos
<= size());
366 return owner_
->getChar(pos
);
370 void Paragraph::Private::insertChar(pos_type pos
, value_type c
,
371 Change
const & change
)
373 BOOST_ASSERT(pos
>= 0 && pos
<= size());
376 changes_
.insert(change
, pos
);
378 // This is actually very common when parsing buffers (and
379 // maybe inserting ascii text)
381 // when appending characters, no need to update tables
382 owner_
->text_
.push_back(c
);
386 owner_
->text_
.insert(owner_
->text_
.begin() + pos
, c
);
388 // Update the font table.
389 fontlist_
.increasePosAfterPos(pos
);
392 insetlist_
.increasePosAfterPos(pos
);
396 void Paragraph::insertInset(pos_type pos
, Inset
* inset
,
397 Change
const & change
)
400 BOOST_ASSERT(pos
>= 0 && pos
<= size());
402 d
->insertChar(pos
, META_INSET
, change
);
403 BOOST_ASSERT(text_
[pos
] == META_INSET
);
405 // Add a new entry in the insetlist_.
406 d
->insetlist_
.insert(inset
, pos
);
410 bool Paragraph::eraseChar(pos_type pos
, bool trackChanges
)
412 BOOST_ASSERT(pos
>= 0 && pos
<= size());
414 // keep the logic here in sync with the logic of isMergedOnEndOfParDeletion()
417 Change change
= d
->changes_
.lookup(pos
);
419 // set the character to DELETED if
420 // a) it was previously unchanged or
421 // b) it was inserted by a co-author
423 if (change
.type
== Change::UNCHANGED
||
424 (change
.type
== Change::INSERTED
&& change
.author
!= 0)) {
425 setChange(pos
, Change(Change::DELETED
));
429 if (change
.type
== Change::DELETED
)
433 // Don't physically access the imaginary end-of-paragraph character.
434 // eraseChar() can only mark it as DELETED. A physical deletion of
435 // end-of-par must be handled externally.
441 d
->changes_
.erase(pos
);
443 // if it is an inset, delete the inset entry
444 if (text_
[pos
] == Paragraph::META_INSET
)
445 d
->insetlist_
.erase(pos
);
447 text_
.erase(text_
.begin() + pos
);
449 // Update the fontlist_
450 d
->fontlist_
.erase(pos
);
452 // Update the insetlist_
453 d
->insetlist_
.decreasePosAfterPos(pos
);
459 int Paragraph::eraseChars(pos_type start
, pos_type end
, bool trackChanges
)
461 BOOST_ASSERT(start
>= 0 && start
<= size());
462 BOOST_ASSERT(end
>= start
&& end
<= size() + 1);
465 for (pos_type count
= end
- start
; count
; --count
) {
466 if (!eraseChar(i
, trackChanges
))
473 int Paragraph::Private::latexSurrogatePair(odocstream
& os
, value_type c
,
474 value_type next
, Encoding
const & encoding
)
476 // Writing next here may circumvent a possible font change between
477 // c and next. Since next is only output if it forms a surrogate pair
478 // with c we can ignore this:
479 // A font change inside a surrogate pair does not make sense and is
480 // hopefully impossible to input.
481 // FIXME: change tracking
482 // Is this correct WRT change tracking?
483 docstring
const latex1
= encoding
.latexChar(next
);
484 docstring
const latex2
= encoding
.latexChar(c
);
485 os
<< latex1
<< '{' << latex2
<< '}';
486 return latex1
.length() + latex2
.length() + 2;
490 bool Paragraph::Private::simpleTeXBlanks(Encoding
const & encoding
,
491 odocstream
& os
, TexRow
& texrow
,
493 unsigned int & column
,
495 Layout
const & style
)
500 if (i
+ 1 < size()) {
501 char_type next
= getChar(i
+ 1);
502 if (Encodings::isCombiningChar(next
)) {
503 // This space has an accent, so we must always output it.
504 column
+= latexSurrogatePair(os
, ' ', next
, encoding
) - 1;
509 if (lyxrc
.plaintext_linelen
> 0
510 && column
> lyxrc
.plaintext_linelen
512 && getChar(i
- 1) != ' '
514 // same in FreeSpacing mode
515 && !owner_
->isFreeSpacing()
516 // In typewriter mode, we want to avoid
517 // ! . ? : at the end of a line
518 && !(font
.family() == Font::TYPEWRITER_FAMILY
519 && (getChar(i
- 1) == '.'
520 || getChar(i
- 1) == '?'
521 || getChar(i
- 1) == ':'
522 || getChar(i
- 1) == '!'))) {
525 texrow
.start(owner_
->id(), i
+ 1);
527 } else if (style
.free_spacing
) {
536 int Paragraph::Private::knownLangChars(odocstream
& os
,
539 Change
& runningChange
,
540 Encoding
const & encoding
,
543 // When the character is marked by the proper language, we simply
544 // get its code point in some encoding, otherwise we get the
545 // translation specified in the unicodesymbols file, which is
546 // something like "\textLANG{<spec>}". So, we have to retain
547 // "\textLANG{<spec>" for the first char but only "<spec>" for
548 // all subsequent chars.
549 docstring
const latex1
= rtrim(encoding
.latexChar(c
), "}");
550 int length
= latex1
.length();
552 while (i
+ 1 < size()) {
553 char_type next
= getChar(i
+ 1);
554 // Stop here if next character belongs to another
555 // language or there is a change tracking status.
556 if (!Encodings::isKnownLangChar(next
, preamble
) ||
557 runningChange
!= owner_
->lookupChange(i
+ 1))
561 FontList::const_iterator cit
= fontlist_
.begin();
562 FontList::const_iterator end
= fontlist_
.end();
563 for (; cit
!= end
; ++cit
) {
564 if (cit
->pos() >= i
&& !found
) {
565 prev_font
= cit
->font();
568 if (cit
->pos() >= i
+ 1)
571 // Stop here if there is a font attribute change.
572 if (found
&& cit
!= end
&& prev_font
!= cit
->font())
574 docstring
const latex
= rtrim(encoding
.latexChar(next
), "}");
575 docstring::size_type
const j
=
576 latex
.find_first_of(from_ascii("{"));
577 if (j
== docstring::npos
) {
579 length
+= latex
.length();
581 os
<< latex
.substr(j
+ 1);
582 length
+= latex
.substr(j
+ 1).length();
586 // When the proper language is set, we are simply passed a code
587 // point, so we should not try to close the \textLANG command.
588 if (prefixIs(latex1
, from_ascii("\\" + preamble
))) {
596 bool Paragraph::Private::isTextAt(string
const & str
, pos_type pos
) const
598 pos_type
const len
= str
.length();
600 // is the paragraph large enough?
601 if (pos
+ len
> size())
604 // does the wanted text start at point?
605 for (string::size_type i
= 0; i
< str
.length(); ++i
) {
606 // Caution: direct comparison of characters works only
607 // because str is pure ASCII.
608 if (str
[i
] != owner_
->text_
[pos
+ i
])
612 return fontlist_
.hasChangeInRange(pos
, len
);
616 void Paragraph::Private::latexInset(Buffer
const & buf
,
617 BufferParams
const & bparams
,
620 OutputParams
& runparams
,
623 Font
const & outerfont
,
625 Change
& running_change
,
626 Layout
const & style
,
628 unsigned int & column
)
630 Inset
* inset
= owner_
->getInset(i
);
633 if (style
.pass_thru
) {
634 inset
->plaintext(buf
, os
, runparams
);
638 // FIXME: move this to InsetNewline::latex
639 if (inset
->lyxCode() == NEWLINE_CODE
) {
640 // newlines are handled differently here than
641 // the default in simpleTeXSpecialChars().
642 if (!style
.newline_allowed
) {
646 column
+= running_font
.latexWriteEndChanges(
647 os
, bparams
, runparams
,
652 if (running_font
.family() == Font::TYPEWRITER_FAMILY
)
655 basefont
= owner_
->getLayoutFont(bparams
, outerfont
);
656 running_font
= basefont
;
658 if (runparams
.moving_arg
)
664 texrow
.start(owner_
->id(), i
+ 1);
668 if (owner_
->lookupChange(i
).type
== Change::DELETED
) {
669 if( ++runparams
.inDeletedInset
== 1)
670 runparams
.changeOfDeletedInset
= owner_
->lookupChange(i
);
673 if (inset
->canTrackChanges()) {
674 column
+= Changes::latexMarkChange(os
, bparams
, running_change
,
675 Change(Change::UNCHANGED
));
676 running_change
= Change(Change::UNCHANGED
);
680 odocstream::pos_type
const len
= os
.tellp();
682 if ((inset
->lyxCode() == GRAPHICS_CODE
683 || inset
->lyxCode() == MATH_CODE
684 || inset
->lyxCode() == HYPERLINK_CODE
)
685 && running_font
.isRightToLeft()) {
686 if (running_font
.language()->lang() == "farsi")
693 // FIXME: Bug: we can have an empty font change here!
694 // if there has just been a font change, we are going to close it
695 // right now, which means stupid latex code like \textsf{}. AFAIK,
696 // this does not harm dvi output. A minor bug, thus (JMarc)
698 // Some insets cannot be inside a font change command.
699 // However, even such insets *can* be placed in \L or \R
700 // or their equivalents (for RTL language switches), so we don't
701 // close the language in those cases.
702 // ArabTeX, though, cannot handle this special behavior, it seems.
703 bool arabtex
= basefont
.language()->lang() == "arabic_arabtex"
704 || running_font
.language()->lang() == "arabic_arabtex";
705 if (open_font
&& inset
->noFontChange()) {
706 bool closeLanguage
= arabtex
707 || basefont
.isRightToLeft() == running_font
.isRightToLeft();
708 unsigned int count
= running_font
.latexWriteEndChanges(os
,
709 bparams
, runparams
, basefont
, basefont
, closeLanguage
);
711 // if any font properties were closed, update the running_font,
712 // making sure, however, to leave the language as it was
714 // FIXME: probably a better way to keep track of the old
715 // language, than copying the entire font?
716 Font
const copy_font(running_font
);
717 basefont
= owner_
->getLayoutFont(bparams
, outerfont
);
718 running_font
= basefont
;
720 running_font
.setLanguage(copy_font
.language());
721 // leave font open if language is still open
722 open_font
= (running_font
.language() == basefont
.language());
724 runparams
.local_font
= &basefont
;
728 int tmp
= inset
->latex(buf
, os
, runparams
);
731 if (running_font
.language()->lang() == "farsi")
738 for (int j
= 0; j
< tmp
; ++j
)
741 texrow
.start(owner_
->id(), i
+ 1);
744 column
+= os
.tellp() - len
;
747 if (owner_
->lookupChange(i
).type
== Change::DELETED
)
748 --runparams
.inDeletedInset
;
752 void Paragraph::Private::latexSpecialChar(
754 OutputParams
& runparams
,
756 Change
& running_change
,
757 Layout
const & style
,
759 unsigned int & column
)
761 value_type
const c
= getChar(i
);
763 if (style
.pass_thru
) {
765 // FIXME UNICODE: This can fail if c cannot
766 // be encoded in the current encoding.
771 if (runparams
.verbatim
) {
778 os
<< "\\textbackslash{}";
785 // In T1 encoding, these characters exist
786 if (lyxrc
.fontenc
== "T1") {
788 //... but we should avoid ligatures
789 if ((c
== '>' || c
== '<')
791 && getChar(i
+ 1) == c
) {
792 //os << "\\textcompwordmark{}";
794 // Jean-Marc, have a look at
795 // this. I think this works
803 // Typewriter font also has them
804 if (running_font
.family() == Font::TYPEWRITER_FAMILY
) {
808 // Otherwise, we use what LaTeX
812 os
<< "\\textless{}";
816 os
<< "\\textgreater{}";
826 case '-': // "--" in Typewriter mode -> "-{}-"
827 if (i
<= size() - 2 &&
828 getChar(i
+ 1) == '-' &&
829 running_font
.family() == Font::TYPEWRITER_FAMILY
) {
838 os
<< "\\char`\\\"{}";
843 case '%': case '#': case '{':
851 os
<< "\\textasciitilde{}";
856 os
<< "\\textasciicircum{}";
861 // avoid being mistaken for optional arguments
869 // Blanks are printed before font switching.
870 // Sure? I am not! (try nice-latex)
871 // I am sure it's correct. LyX might be smarter
872 // in the future, but for now, nothing wrong is
877 // I assume this is hack treating typewriter as verbatim
878 // FIXME UNICODE: This can fail if c cannot be encoded
879 // in the current encoding.
880 if (running_font
.family() == Font::TYPEWRITER_FAMILY
) {
888 // FIXME: if we have "LaTeX" with a font
889 // change in the middle (before the 'T', then
890 // the "TeX" part is still special cased.
891 // Really we should only operate this on
892 // "words" for some definition of word
896 for (; pnr
< phrases_nr
; ++pnr
) {
897 if (isTextAt(special_phrases
[pnr
].phrase
, i
)) {
898 os
<< special_phrases
[pnr
].macro
;
899 i
+= special_phrases
[pnr
].phrase
.length() - 1;
900 column
+= special_phrases
[pnr
].macro
.length() - 1;
905 if (pnr
== phrases_nr
&& c
!= '\0') {
906 Encoding
const & encoding
= *(runparams
.encoding
);
907 if (i
+ 1 < size()) {
908 char_type next
= getChar(i
+ 1);
909 if (Encodings::isCombiningChar(next
)) {
910 column
+= latexSurrogatePair(os
, c
, next
, encoding
) - 1;
916 if (Encodings::isKnownLangChar(c
, preamble
)) {
917 column
+= knownLangChars(os
, c
, preamble
, running_change
,
921 docstring
const latex
= encoding
.latexChar(c
);
922 if (latex
.length() > 1 && latex
[latex
.length() - 1] != '}') {
923 // Prevent eating of a following
924 // space or command corruption by
925 // following characters
926 column
+= latex
.length() + 1;
929 column
+= latex
.length() - 1;
938 void Paragraph::Private::validate(LaTeXFeatures
& features
,
939 Layout
const & layout
) const
942 if (!params_
.spacing().isDefault())
943 features
.require("setspace");
946 features
.useLayout(layout
.name());
949 fontlist_
.validate(features
);
951 // then the indentation
952 if (!params_
.leftIndent().zero())
953 features
.require("ParagraphLeftIndent");
956 InsetList::const_iterator icit
= insetlist_
.begin();
957 InsetList::const_iterator iend
= insetlist_
.end();
958 for (; icit
!= iend
; ++icit
) {
960 icit
->inset
->validate(features
);
961 if (layout
.needprotect
&&
962 icit
->inset
->lyxCode() == FOOT_CODE
)
963 features
.require("NeedLyXFootnoteCode");
968 for (pos_type i
= 0; i
< size() ; ++i
) {
969 for (size_t pnr
= 0; pnr
< phrases_nr
; ++pnr
) {
970 if (!special_phrases
[pnr
].builtin
971 && isTextAt(special_phrases
[pnr
].phrase
, i
)) {
972 features
.require(special_phrases
[pnr
].phrase
);
976 Encodings::validate(getChar(i
), features
);
980 /////////////////////////////////////////////////////////////////////
984 /////////////////////////////////////////////////////////////////////
986 Paragraph::Paragraph()
987 : begin_of_body_(0), d(new Paragraph::Private(this))
995 Paragraph::Paragraph(Paragraph
const & par
)
996 : itemdepth(par
.itemdepth
),
997 layout_(par
.layout_
),
998 text_(par
.text_
), begin_of_body_(par
.begin_of_body_
),
999 d(new Paragraph::Private(*par
.d
, this))
1004 Paragraph
& Paragraph::operator=(Paragraph
const & par
)
1006 // needed as we will destroy the private part before copying it
1008 itemdepth
= par
.itemdepth
;
1009 layout_
= par
.layout();
1011 begin_of_body_
= par
.begin_of_body_
;
1014 d
= new Private(*par
.d
, this);
1020 Paragraph::~Paragraph()
1026 void Paragraph::write(Buffer
const & buf
, ostream
& os
,
1027 BufferParams
const & bparams
,
1028 depth_type
& dth
) const
1030 // The beginning or end of a deeper (i.e. nested) area?
1031 if (dth
!= params().depth()) {
1032 if (params().depth() > dth
) {
1033 while (params().depth() > dth
) {
1034 os
<< "\n\\begin_deeper";
1038 while (params().depth() < dth
) {
1039 os
<< "\n\\end_deeper";
1045 // First write the layout
1046 os
<< "\n\\begin_layout " << to_utf8(layout()->name()) << '\n';
1050 Font
font1(Font::ALL_INHERIT
, bparams
.language
);
1052 Change running_change
= Change(Change::UNCHANGED
);
1055 for (pos_type i
= 0; i
<= size(); ++i
) {
1057 Change change
= lookupChange(i
);
1058 Changes::lyxMarkChange(os
, column
, running_change
, change
);
1059 running_change
= change
;
1064 // Write font changes
1065 Font font2
= getFontSettings(bparams
, i
);
1066 if (font2
!= font1
) {
1067 font2
.lyxWriteChanges(font1
, os
);
1072 value_type
const c
= getChar(i
);
1076 Inset
const * inset
= getInset(i
);
1078 if (inset
->directWrite()) {
1079 // international char, let it write
1080 // code directly so it's shorter in
1082 inset
->write(buf
, os
);
1086 os
<< "\\begin_inset ";
1087 inset
->write(buf
, os
);
1088 os
<< "\n\\end_inset\n\n";
1094 os
<< "\n\\backslash\n";
1098 if (i
+ 1 < size() && getChar(i
+ 1) == ' ') {
1105 if ((column
> 70 && c
== ' ')
1110 // this check is to amend a bug. LyX sometimes
1111 // inserts '\0' this could cause problems.
1113 std::vector
<char> tmp
= ucs4_to_utf8(c
);
1114 tmp
.push_back('\0');
1117 lyxerr
<< "ERROR (Paragraph::writeFile):"
1118 " NULL char in structure." << endl
;
1124 os
<< "\n\\end_layout\n";
1128 void Paragraph::validate(LaTeXFeatures
& features
) const
1130 d
->validate(features
, *layout());
1134 void Paragraph::insert(pos_type start
, docstring
const & str
,
1135 Font
const & font
, Change
const & change
)
1137 for (size_t i
= 0, n
= str
.size(); i
!= n
; ++i
)
1138 insertChar(start
+ i
, str
[i
], font
, change
);
1142 void Paragraph::appendChar(value_type c
, Font
const & font
,
1143 Change
const & change
)
1146 d
->changes_
.insert(change
, text_
.size());
1147 // when appending characters, no need to update tables
1149 setFont(text_
.size() - 1, font
);
1153 void Paragraph::appendString(docstring
const & s
, Font
const & font
,
1154 Change
const & change
)
1156 size_t end
= s
.size();
1157 size_t oldsize
= text_
.size();
1158 size_t newsize
= oldsize
+ end
;
1159 size_t capacity
= text_
.capacity();
1160 if (newsize
>= capacity
)
1161 text_
.reserve(std::max(capacity
+ 100, newsize
));
1163 // FIXME: Optimize this!
1164 for (pos_type i
= 0; i
!= end
; ++i
) {
1166 d
->changes_
.insert(change
, i
);
1167 // when appending characters, no need to update tables
1168 text_
.push_back(s
[i
]);
1170 d
->fontlist_
.setRange(oldsize
, newsize
, font
);
1174 void Paragraph::insertChar(pos_type pos
, Paragraph::value_type c
,
1177 d
->insertChar(pos
, c
, Change(trackChanges
?
1178 Change::INSERTED
: Change::UNCHANGED
));
1182 void Paragraph::insertChar(pos_type pos
, Paragraph::value_type c
,
1183 Font
const & font
, bool trackChanges
)
1185 d
->insertChar(pos
, c
, Change(trackChanges
?
1186 Change::INSERTED
: Change::UNCHANGED
));
1191 void Paragraph::insertChar(pos_type pos
, Paragraph::value_type c
,
1192 Font
const & font
, Change
const & change
)
1194 d
->insertChar(pos
, c
, change
);
1199 void Paragraph::insertInset(pos_type pos
, Inset
* inset
,
1200 Font
const & font
, Change
const & change
)
1202 insertInset(pos
, inset
, change
);
1203 // Set the font/language of the inset...
1208 bool Paragraph::insetAllowed(InsetCode code
)
1210 return !d
->inset_owner_
|| d
->inset_owner_
->insetAllowed(code
);
1214 // Gets uninstantiated font setting at position.
1215 Font
const Paragraph::getFontSettings(BufferParams
const & bparams
,
1219 lyxerr
<< " pos: " << pos
<< " size: " << size() << endl
;
1220 BOOST_ASSERT(pos
<= size());
1223 FontList::const_iterator cit
= d
->fontlist_
.fontIterator(pos
);
1224 if (cit
!= d
->fontlist_
.end())
1227 if (pos
== size() && !empty())
1228 return getFontSettings(bparams
, pos
- 1);
1230 return Font(Font::ALL_INHERIT
, getParLanguage(bparams
));
1234 FontSpan
Paragraph::fontSpan(pos_type pos
) const
1236 BOOST_ASSERT(pos
<= size());
1239 FontList::const_iterator cit
= d
->fontlist_
.begin();
1240 FontList::const_iterator end
= d
->fontlist_
.end();
1241 for (; cit
!= end
; ++cit
) {
1242 if (cit
->pos() >= pos
) {
1243 if (pos
>= beginOfBody())
1244 return FontSpan(std::max(start
, beginOfBody()),
1247 return FontSpan(start
,
1248 std::min(beginOfBody() - 1,
1251 start
= cit
->pos() + 1;
1254 // This should not happen, but if so, we take no chances.
1255 //lyxerr << "Paragraph::getEndPosOfFontSpan: This should not happen!"
1257 return FontSpan(pos
, pos
);
1261 // Gets uninstantiated font setting at position 0
1262 Font
const Paragraph::getFirstFontSettings(BufferParams
const & bparams
) const
1264 if (!empty() && !d
->fontlist_
.empty())
1265 return d
->fontlist_
.begin()->font();
1267 return Font(Font::ALL_INHERIT
, bparams
.language
);
1271 // Gets the fully instantiated font at a given position in a paragraph
1272 // This is basically the same function as Text::GetFont() in text2.cpp.
1273 // The difference is that this one is used for generating the LaTeX file,
1274 // and thus cosmetic "improvements" are disallowed: This has to deliver
1275 // the true picture of the buffer. (Asger)
1276 Font
const Paragraph::getFont(BufferParams
const & bparams
, pos_type pos
,
1277 Font
const & outerfont
) const
1279 BOOST_ASSERT(pos
>= 0);
1281 Font font
= getFontSettings(bparams
, pos
);
1283 pos_type
const body_pos
= beginOfBody();
1285 font
.realize(layout_
->labelfont
);
1287 font
.realize(layout_
->font
);
1289 font
.realize(outerfont
);
1290 font
.realize(bparams
.getFont());
1296 Font
const Paragraph::getLabelFont
1297 (BufferParams
const & bparams
, Font
const & outerfont
) const
1299 Font tmpfont
= layout()->labelfont
;
1300 tmpfont
.setLanguage(getParLanguage(bparams
));
1301 tmpfont
.realize(outerfont
);
1302 tmpfont
.realize(bparams
.getFont());
1307 Font
const Paragraph::getLayoutFont
1308 (BufferParams
const & bparams
, Font
const & outerfont
) const
1310 Font tmpfont
= layout()->font
;
1311 tmpfont
.setLanguage(getParLanguage(bparams
));
1312 tmpfont
.realize(outerfont
);
1313 tmpfont
.realize(bparams
.getFont());
1318 /// Returns the height of the highest font in range
1319 Font_size
Paragraph::highestFontInRange
1320 (pos_type startpos
, pos_type endpos
, Font_size def_size
) const
1322 return d
->fontlist_
.highestInRange(startpos
, endpos
, def_size
);
1326 Paragraph::value_type
1327 Paragraph::getUChar(BufferParams
const & bparams
, pos_type pos
) const
1329 value_type c
= getChar(pos
);
1330 if (!lyxrc
.rtl_support
)
1360 if (uc
!= c
&& getFontSettings(bparams
, pos
).isRightToLeft())
1367 void Paragraph::setFont(pos_type pos
, Font
const & font
)
1369 BOOST_ASSERT(pos
<= size());
1371 // First, reduce font against layout/label font
1372 // Update: The setCharFont() routine in text2.cpp already
1373 // reduces font, so we don't need to do that here. (Asger)
1375 d
->fontlist_
.set(pos
, font
);
1379 void Paragraph::makeSameLayout(Paragraph
const & par
)
1381 layout(par
.layout());
1383 d
->params_
= par
.params();
1387 bool Paragraph::stripLeadingSpaces(bool trackChanges
)
1389 if (isFreeSpacing())
1395 while (pos
< size() && (isNewline(pos
) || isLineSeparator(pos
))) {
1396 if (eraseChar(pos
, trackChanges
))
1402 return count
> 0 || pos
> 0;
1406 bool Paragraph::hasSameLayout(Paragraph
const & par
) const
1408 return par
.layout() == layout() && d
->params_
.sameLayout(par
.params());
1412 depth_type
Paragraph::getDepth() const
1414 return params().depth();
1418 depth_type
Paragraph::getMaxDepthAfter() const
1420 if (layout()->isEnvironment())
1421 return params().depth() + 1;
1423 return params().depth();
1427 char Paragraph::getAlign() const
1429 if (params().align() == LYX_ALIGN_LAYOUT
)
1430 return layout()->align
;
1432 return params().align();
1436 docstring
const & Paragraph::getLabelstring() const
1438 return params().labelString();
1442 // the next two functions are for the manual labels
1443 docstring
const Paragraph::getLabelWidthString() const
1445 if (layout()->margintype
== MARGIN_MANUAL
)
1446 return params().labelWidthString();
1448 return _("Senseless with this layout!");
1452 void Paragraph::setLabelWidthString(docstring
const & s
)
1454 params().labelWidthString(s
);
1458 docstring
const Paragraph::translateIfPossible(docstring
const & s
,
1459 BufferParams
const & bparams
) const
1461 if (!support::isAscii(s
) || s
.empty()) {
1462 // This must be a user defined layout. We cannot translate
1463 // this, since gettext accepts only ascii keys.
1466 // Probably standard layout, try to translate
1467 Messages
& m
= getMessages(getParLanguage(bparams
)->code());
1468 return m
.get(to_ascii(s
));
1472 docstring
Paragraph::expandLabel(LayoutPtr
const & layout
,
1473 BufferParams
const & bparams
, bool process_appendix
) const
1475 TextClass
const & tclass
= bparams
.getTextClass();
1478 if (process_appendix
&& params().appendix())
1479 fmt
= translateIfPossible(layout
->labelstring_appendix(),
1482 fmt
= translateIfPossible(layout
->labelstring(), bparams
);
1484 if (fmt
.empty() && layout
->labeltype
== LABEL_COUNTER
1485 && !layout
->counter
.empty())
1486 fmt
= "\\the" + layout
->counter
;
1488 // handle 'inherited level parts' in 'fmt',
1489 // i.e. the stuff between '@' in '@Section@.\arabic{subsection}'
1490 size_t const i
= fmt
.find('@', 0);
1491 if (i
!= docstring::npos
) {
1492 size_t const j
= fmt
.find('@', i
+ 1);
1493 if (j
!= docstring::npos
) {
1494 docstring
parent(fmt
, i
+ 1, j
- i
- 1);
1495 docstring label
= from_ascii("??");
1496 if (tclass
.hasLayout(parent
))
1497 docstring label
= expandLabel(tclass
[parent
], bparams
,
1499 fmt
= docstring(fmt
, 0, i
) + label
1500 + docstring(fmt
, j
+ 1, docstring::npos
);
1504 return tclass
.counters().counterLabel(fmt
);
1508 void Paragraph::applyLayout(LayoutPtr
const & new_layout
)
1511 LyXAlignment
const oldAlign
= params().align();
1513 if (!(oldAlign
& layout()->alignpossible
)) {
1514 frontend::Alert::warning(_("Alignment not permitted"),
1515 _("The new layout does not permit the alignment previously used.\nSetting to default."));
1516 params().align(LYX_ALIGN_LAYOUT
);
1521 pos_type
Paragraph::beginOfBody() const
1523 return begin_of_body_
;
1527 void Paragraph::setBeginOfBody()
1529 if (layout()->labeltype
!= LABEL_MANUAL
) {
1534 // Unroll the first two cycles of the loop
1535 // and remember the previous character to
1536 // remove unnecessary getChar() calls
1538 pos_type end
= size();
1539 if (i
< end
&& !isNewline(i
)) {
1541 char_type previous_char
= 0;
1544 previous_char
= text_
[i
];
1545 if (!isNewline(i
)) {
1547 while (i
< end
&& previous_char
!= ' ') {
1552 previous_char
= temp
;
1562 // returns -1 if inset not found
1563 int Paragraph::getPositionOfInset(Inset
const * inset
) const
1566 InsetList::const_iterator it
= d
->insetlist_
.begin();
1567 InsetList::const_iterator end
= d
->insetlist_
.end();
1568 for (; it
!= end
; ++it
)
1569 if (it
->inset
== inset
)
1575 InsetBibitem
* Paragraph::bibitem() const
1577 if (!d
->insetlist_
.empty()) {
1578 Inset
* inset
= d
->insetlist_
.begin()->inset
;
1579 if (inset
->lyxCode() == BIBITEM_CODE
)
1580 return static_cast<InsetBibitem
*>(inset
);
1586 bool Paragraph::forceDefaultParagraphs() const
1588 return inInset() && inInset()->forceDefaultParagraphs(0);
1594 // paragraphs inside floats need different alignment tags to avoid
1597 bool noTrivlistCentering(InsetCode code
)
1599 return code
== FLOAT_CODE
|| code
== WRAP_CODE
;
1603 string
correction(string
const & orig
)
1605 if (orig
== "flushleft")
1606 return "raggedright";
1607 if (orig
== "flushright")
1608 return "raggedleft";
1609 if (orig
== "center")
1615 string
const corrected_env(string
const & suffix
, string
const & env
,
1618 string output
= suffix
+ "{";
1619 if (noTrivlistCentering(code
))
1620 output
+= correction(env
);
1624 if (suffix
== "\\begin")
1630 void adjust_row_column(string
const & str
, TexRow
& texrow
, int & column
)
1632 if (!contains(str
, "\n"))
1633 column
+= str
.size();
1637 column
= rsplit(str
, tmp
, '\n').size();
1644 // This could go to ParagraphParameters if we want to
1645 int Paragraph::startTeXParParams(BufferParams
const & bparams
,
1646 odocstream
& os
, TexRow
& texrow
,
1647 bool moving_arg
) const
1651 if (params().noindent()) {
1652 os
<< "\\noindent ";
1656 LyXAlignment
const curAlign
= params().align();
1658 if (curAlign
== layout()->align
)
1662 case LYX_ALIGN_NONE
:
1663 case LYX_ALIGN_BLOCK
:
1664 case LYX_ALIGN_LAYOUT
:
1665 case LYX_ALIGN_SPECIAL
:
1667 case LYX_ALIGN_LEFT
:
1668 case LYX_ALIGN_RIGHT
:
1669 case LYX_ALIGN_CENTER
:
1678 case LYX_ALIGN_NONE
:
1679 case LYX_ALIGN_BLOCK
:
1680 case LYX_ALIGN_LAYOUT
:
1681 case LYX_ALIGN_SPECIAL
:
1683 case LYX_ALIGN_LEFT
: {
1685 if (getParLanguage(bparams
)->babel() != "hebrew")
1686 output
= corrected_env("\\begin", "flushleft", ownerCode());
1688 output
= corrected_env("\\begin", "flushright", ownerCode());
1689 os
<< from_ascii(output
);
1690 adjust_row_column(output
, texrow
, column
);
1692 } case LYX_ALIGN_RIGHT
: {
1694 if (getParLanguage(bparams
)->babel() != "hebrew")
1695 output
= corrected_env("\\begin", "flushright", ownerCode());
1697 output
= corrected_env("\\begin", "flushleft", ownerCode());
1698 os
<< from_ascii(output
);
1699 adjust_row_column(output
, texrow
, column
);
1701 } case LYX_ALIGN_CENTER
: {
1703 output
= corrected_env("\\begin", "center", ownerCode());
1704 os
<< from_ascii(output
);
1705 adjust_row_column(output
, texrow
, column
);
1714 // This could go to ParagraphParameters if we want to
1715 int Paragraph::endTeXParParams(BufferParams
const & bparams
,
1716 odocstream
& os
, TexRow
& texrow
,
1717 bool moving_arg
) const
1721 switch (params().align()) {
1722 case LYX_ALIGN_NONE
:
1723 case LYX_ALIGN_BLOCK
:
1724 case LYX_ALIGN_LAYOUT
:
1725 case LYX_ALIGN_SPECIAL
:
1727 case LYX_ALIGN_LEFT
:
1728 case LYX_ALIGN_RIGHT
:
1729 case LYX_ALIGN_CENTER
:
1737 switch (params().align()) {
1738 case LYX_ALIGN_NONE
:
1739 case LYX_ALIGN_BLOCK
:
1740 case LYX_ALIGN_LAYOUT
:
1741 case LYX_ALIGN_SPECIAL
:
1743 case LYX_ALIGN_LEFT
: {
1745 if (getParLanguage(bparams
)->babel() != "hebrew")
1746 output
= corrected_env("\n\\par\\end", "flushleft", ownerCode());
1748 output
= corrected_env("\n\\par\\end", "flushright", ownerCode());
1749 os
<< from_ascii(output
);
1750 adjust_row_column(output
, texrow
, column
);
1752 } case LYX_ALIGN_RIGHT
: {
1754 if (getParLanguage(bparams
)->babel() != "hebrew")
1755 output
= corrected_env("\n\\par\\end", "flushright", ownerCode());
1757 output
= corrected_env("\n\\par\\end", "flushleft", ownerCode());
1758 os
<< from_ascii(output
);
1759 adjust_row_column(output
, texrow
, column
);
1761 } case LYX_ALIGN_CENTER
: {
1763 output
= corrected_env("\n\\par\\end", "center", ownerCode());
1764 os
<< from_ascii(output
);
1765 adjust_row_column(output
, texrow
, column
);
1774 // This one spits out the text of the paragraph
1775 bool Paragraph::latex(Buffer
const & buf
,
1776 BufferParams
const & bparams
,
1777 Font
const & outerfont
,
1778 odocstream
& os
, TexRow
& texrow
,
1779 OutputParams
const & runparams
) const
1781 LYXERR(Debug::LATEX
) << "SimpleTeXOnePar... " << this << endl
;
1783 bool return_value
= false;
1787 // well we have to check if we are in an inset with unlimited
1788 // length (all in one row) if that is true then we don't allow
1789 // any special options in the paragraph and also we don't allow
1790 // any environment other than the default layout of the text class
1792 bool asdefault
= forceDefaultParagraphs();
1795 style
= bparams
.getTextClass().defaultLayout();
1800 // Current base font for all inherited font changes, without any
1801 // change caused by an individual character, except for the language:
1802 // It is set to the language of the first character.
1803 // As long as we are in the label, this font is the base font of the
1804 // label. Before the first body character it is set to the base font
1808 // Maybe we have to create a optional argument.
1809 pos_type body_pos
= beginOfBody();
1810 unsigned int column
= 0;
1813 // the optional argument is kept in curly brackets in
1814 // case it contains a ']'
1817 basefont
= getLabelFont(bparams
, outerfont
);
1819 basefont
= getLayoutFont(bparams
, outerfont
);
1822 // Which font is currently active?
1823 Font
running_font(basefont
);
1824 // Do we have an open font change?
1825 bool open_font
= false;
1827 Change runningChange
= Change(Change::UNCHANGED
);
1829 texrow
.start(id(), 0);
1831 // if the paragraph is empty, the loop will not be entered at all
1833 if (style
->isCommand()) {
1838 column
+= startTeXParParams(bparams
, os
, texrow
,
1839 runparams
.moving_arg
);
1842 for (pos_type i
= 0; i
< size(); ++i
) {
1843 // First char in paragraph or after label?
1844 if (i
== body_pos
) {
1847 column
+= running_font
.latexWriteEndChanges(
1848 os
, bparams
, runparams
,
1849 basefont
, basefont
);
1852 basefont
= getLayoutFont(bparams
, outerfont
);
1853 running_font
= basefont
;
1855 column
+= Changes::latexMarkChange(os
, bparams
,
1856 runningChange
, Change(Change::UNCHANGED
));
1857 runningChange
= Change(Change::UNCHANGED
);
1862 if (style
->isCommand()) {
1868 column
+= startTeXParParams(bparams
, os
,
1870 runparams
.moving_arg
);
1873 Change
const & change
= runparams
.inDeletedInset
? runparams
.changeOfDeletedInset
1876 if (bparams
.outputChanges
&& runningChange
!= change
) {
1878 column
+= running_font
.latexWriteEndChanges(
1879 os
, bparams
, runparams
, basefont
, basefont
);
1882 basefont
= getLayoutFont(bparams
, outerfont
);
1883 running_font
= basefont
;
1885 column
+= Changes::latexMarkChange(os
, bparams
, runningChange
, change
);
1886 runningChange
= change
;
1889 // do not output text which is marked deleted
1890 // if change tracking output is disabled
1891 if (!bparams
.outputChanges
&& change
.type
== Change::DELETED
) {
1897 // Fully instantiated font
1898 Font
const font
= getFont(bparams
, i
, outerfont
);
1900 Font
const last_font
= running_font
;
1902 // Do we need to close the previous font?
1904 (font
!= running_font
||
1905 font
.language() != running_font
.language()))
1907 column
+= running_font
.latexWriteEndChanges(
1908 os
, bparams
, runparams
, basefont
,
1909 (i
== body_pos
-1) ? basefont
: font
);
1910 running_font
= basefont
;
1914 // Switch file encoding if necessary
1915 if (runparams
.encoding
->package() == Encoding::inputenc
&&
1916 font
.language()->encoding()->package() == Encoding::inputenc
) {
1917 std::pair
<bool, int> const enc_switch
= switchEncoding(os
, bparams
,
1918 runparams
.moving_arg
, *(runparams
.encoding
),
1919 *(font
.language()->encoding()));
1920 if (enc_switch
.first
) {
1921 column
+= enc_switch
.second
;
1922 runparams
.encoding
= font
.language()->encoding();
1926 value_type
const c
= getChar(i
);
1928 // Do we need to change font?
1929 if ((font
!= running_font
||
1930 font
.language() != running_font
.language()) &&
1933 odocstringstream ods
;
1934 column
+= font
.latexWriteStartChanges(ods
, bparams
,
1935 runparams
, basefont
,
1937 running_font
= font
;
1939 docstring fontchange
= ods
.str();
1940 // check if the fontchange ends with a trailing blank
1941 // (like "\small " (see bug 3382)
1942 if (suffixIs(fontchange
, ' ') && c
== ' ')
1943 os
<< fontchange
.substr(0, fontchange
.size() - 1)
1944 << from_ascii("{}");
1950 // FIXME: integrate this case in latexSpecialChar
1951 // Do not print the separation of the optional argument
1952 // if style->pass_thru is false. This works because
1953 // latexSpecialChar ignores spaces if
1954 // style->pass_thru is false.
1955 if (i
!= body_pos
- 1) {
1956 if (d
->simpleTeXBlanks(
1957 *(runparams
.encoding
), os
, texrow
,
1958 i
, column
, font
, *style
)) {
1959 // A surrogate pair was output. We
1960 // must not call latexSpecialChar
1961 // in this iteration, since it would output
1962 // the combining character again.
1969 OutputParams rp
= runparams
;
1970 rp
.free_spacing
= style
->free_spacing
;
1971 rp
.local_font
= &font
;
1972 rp
.intitle
= style
->intitle
;
1974 // Two major modes: LaTeX or plain
1975 // Handle here those cases common to both modes
1976 // and then split to handle the two modes separately.
1977 if (c
== Paragraph::META_INSET
)
1978 d
->latexInset(buf
, bparams
, os
,
1979 texrow
, rp
, running_font
,
1980 basefont
, outerfont
, open_font
,
1981 runningChange
, *style
, i
, column
);
1983 d
->latexSpecialChar(os
, rp
, running_font
, runningChange
,
1986 // Set the encoding to that returned from simpleTeXSpecialChars (see
1987 // comment for encoding member in OutputParams.h)
1988 runparams
.encoding
= rp
.encoding
;
1991 // If we have an open font definition, we have to close it
1993 #ifdef FIXED_LANGUAGE_END_DETECTION
1996 .latexWriteEndChanges(os
, bparams
, runparams
,
1998 next_
->getFont(bparams
, 0, outerfont
));
2000 running_font
.latexWriteEndChanges(os
, bparams
,
2001 runparams
, basefont
, basefont
);
2004 //FIXME: For now we ALWAYS have to close the foreign font settings if they are
2005 //FIXME: there as we start another \selectlanguage with the next paragraph if
2006 //FIXME: we are in need of this. This should be fixed sometime (Jug)
2007 running_font
.latexWriteEndChanges(os
, bparams
, runparams
,
2008 basefont
, basefont
);
2012 column
+= Changes::latexMarkChange(os
, bparams
, runningChange
, Change(Change::UNCHANGED
));
2014 // Needed if there is an optional argument but no contents.
2015 if (body_pos
> 0 && body_pos
== size()) {
2017 return_value
= false;
2021 column
+= endTeXParParams(bparams
, os
, texrow
,
2022 runparams
.moving_arg
);
2025 LYXERR(Debug::LATEX
) << "SimpleTeXOnePar...done " << this << endl
;
2026 return return_value
;
2043 string
tag_name(PAR_TAG
const & pt
) {
2045 case PAR_NONE
: return "!-- --";
2046 case TT
: return "tt";
2047 case SF
: return "sf";
2048 case BF
: return "bf";
2049 case IT
: return "it";
2050 case SL
: return "sl";
2051 case EM
: return "em";
2058 void operator|=(PAR_TAG
& p1
, PAR_TAG
const & p2
)
2060 p1
= static_cast<PAR_TAG
>(p1
| p2
);
2065 void reset(PAR_TAG
& p1
, PAR_TAG
const & p2
)
2067 p1
= static_cast<PAR_TAG
>(p1
& ~p2
);
2073 bool Paragraph::emptyTag() const
2075 for (pos_type i
= 0; i
< size(); ++i
) {
2077 Inset
const * inset
= getInset(i
);
2078 InsetCode lyx_code
= inset
->lyxCode();
2079 if (lyx_code
!= TOC_CODE
&&
2080 lyx_code
!= INCLUDE_CODE
&&
2081 lyx_code
!= GRAPHICS_CODE
&&
2082 lyx_code
!= ERT_CODE
&&
2083 lyx_code
!= LISTINGS_CODE
&&
2084 lyx_code
!= FLOAT_CODE
&&
2085 lyx_code
!= TABULAR_CODE
) {
2089 value_type c
= getChar(i
);
2090 if (c
!= ' ' && c
!= '\t')
2098 string
Paragraph::getID(Buffer
const & buf
, OutputParams
const & runparams
) const
2100 for (pos_type i
= 0; i
< size(); ++i
) {
2102 Inset
const * inset
= getInset(i
);
2103 InsetCode lyx_code
= inset
->lyxCode();
2104 if (lyx_code
== LABEL_CODE
) {
2105 string
const id
= static_cast<InsetCommand
const *>(inset
)->getContents();
2106 return "id='" + to_utf8(sgml::cleanID(buf
, runparams
, from_utf8(id
))) + "'";
2115 pos_type
Paragraph::getFirstWord(Buffer
const & buf
, odocstream
& os
, OutputParams
const & runparams
) const
2118 for (i
= 0; i
< size(); ++i
) {
2120 Inset
const * inset
= getInset(i
);
2121 inset
->docbook(buf
, os
, runparams
);
2123 value_type c
= getChar(i
);
2126 os
<< sgml::escapeChar(c
);
2133 bool Paragraph::onlyText(Buffer
const & buf
, Font
const & outerfont
, pos_type initial
) const
2137 for (pos_type i
= initial
; i
< size(); ++i
) {
2138 Font font
= getFont(buf
.params(), i
, outerfont
);
2141 if (i
!= initial
&& font
!= font_old
)
2150 void Paragraph::simpleDocBookOnePar(Buffer
const & buf
,
2152 OutputParams
const & runparams
,
2153 Font
const & outerfont
,
2154 pos_type initial
) const
2156 bool emph_flag
= false;
2158 LayoutPtr
const & style
= layout();
2160 style
->labeltype
== LABEL_MANUAL
? style
->labelfont
: style
->font
;
2162 if (style
->pass_thru
&& !onlyText(buf
, outerfont
, initial
))
2165 // parsing main loop
2166 for (pos_type i
= initial
; i
< size(); ++i
) {
2167 Font font
= getFont(buf
.params(), i
, outerfont
);
2169 // handle <emphasis> tag
2170 if (font_old
.emph() != font
.emph()) {
2171 if (font
.emph() == Font::ON
) {
2174 } else if (i
!= initial
) {
2175 os
<< "</emphasis>";
2181 Inset
const * inset
= getInset(i
);
2182 inset
->docbook(buf
, os
, runparams
);
2184 value_type c
= getChar(i
);
2186 if (style
->pass_thru
)
2189 os
<< sgml::escapeChar(c
);
2195 os
<< "</emphasis>";
2198 if (style
->free_spacing
)
2200 if (style
->pass_thru
&& !onlyText(buf
, outerfont
, initial
))
2205 bool Paragraph::isHfill(pos_type pos
) const
2208 && getInset(pos
)->lyxCode() == HFILL_CODE
;
2212 bool Paragraph::isNewline(pos_type pos
) const
2215 && getInset(pos
)->lyxCode() == NEWLINE_CODE
;
2219 bool Paragraph::isLineSeparator(pos_type pos
) const
2221 value_type
const c
= getChar(pos
);
2222 return isLineSeparatorChar(c
)
2223 || (c
== Paragraph::META_INSET
&& getInset(pos
) &&
2224 getInset(pos
)->isLineSeparator());
2228 /// Used by the spellchecker
2229 bool Paragraph::isLetter(pos_type pos
) const
2232 return getInset(pos
)->isLetter();
2234 value_type
const c
= getChar(pos
);
2235 return isLetterChar(c
) || isDigit(c
);
2241 Paragraph::getParLanguage(BufferParams
const & bparams
) const
2244 return getFirstFontSettings(bparams
).language();
2245 // FIXME: we should check the prev par as well (Lgb)
2246 return bparams
.language
;
2250 bool Paragraph::isRTL(BufferParams
const & bparams
) const
2252 return lyxrc
.rtl_support
2253 && getParLanguage(bparams
)->rightToLeft()
2254 && ownerCode() != ERT_CODE
2255 && ownerCode() != LISTINGS_CODE
;
2259 void Paragraph::changeLanguage(BufferParams
const & bparams
,
2260 Language
const * from
, Language
const * to
)
2262 // change language including dummy font change at the end
2263 for (pos_type i
= 0; i
<= size(); ++i
) {
2264 Font font
= getFontSettings(bparams
, i
);
2265 if (font
.language() == from
) {
2266 font
.setLanguage(to
);
2273 bool Paragraph::isMultiLingual(BufferParams
const & bparams
) const
2275 Language
const * doc_language
= bparams
.language
;
2276 FontList::const_iterator cit
= d
->fontlist_
.begin();
2277 FontList::const_iterator end
= d
->fontlist_
.end();
2279 for (; cit
!= end
; ++cit
)
2280 if (cit
->font().language() != ignore_language
&&
2281 cit
->font().language() != latex_language
&&
2282 cit
->font().language() != doc_language
)
2288 // Convert the paragraph to a string.
2289 // Used for building the table of contents
2290 docstring
const Paragraph::asString(Buffer
const & buffer
, bool label
) const
2292 return asString(buffer
, 0, size(), label
);
2296 docstring
const Paragraph::asString(Buffer
const & buffer
,
2297 pos_type beg
, pos_type end
, bool label
) const
2300 odocstringstream os
;
2302 if (beg
== 0 && label
&& !params().labelString().empty())
2303 os
<< params().labelString() << ' ';
2305 for (pos_type i
= beg
; i
< end
; ++i
) {
2306 value_type
const c
= getChar(i
);
2309 else if (c
== META_INSET
)
2310 getInset(i
)->textString(buffer
, os
);
2317 void Paragraph::setInsetOwner(Inset
* inset
)
2319 d
->inset_owner_
= inset
;
2323 int Paragraph::id() const
2329 LayoutPtr
const & Paragraph::layout() const
2335 void Paragraph::layout(LayoutPtr
const & new_layout
)
2337 layout_
= new_layout
;
2341 Inset
* Paragraph::inInset() const
2343 return d
->inset_owner_
;
2347 InsetCode
Paragraph::ownerCode() const
2349 return d
->inset_owner_
?
2350 d
->inset_owner_
->lyxCode() : NO_CODE
;
2354 ParagraphParameters
& Paragraph::params()
2360 ParagraphParameters
const & Paragraph::params() const
2366 bool Paragraph::isFreeSpacing() const
2368 if (layout()->free_spacing
)
2371 // for now we just need this, later should we need this in some
2372 // other way we can always add a function to Inset too.
2373 return ownerCode() == ERT_CODE
|| ownerCode() == LISTINGS_CODE
;
2377 bool Paragraph::allowEmpty() const
2379 if (layout()->keepempty
)
2381 return ownerCode() == ERT_CODE
|| ownerCode() == LISTINGS_CODE
;
2385 char_type
Paragraph::transformChar(char_type c
, pos_type pos
) const
2387 if (!Encodings::is_arabic(c
))
2390 value_type prev_char
= ' ';
2391 value_type next_char
= ' ';
2393 for (pos_type i
= pos
- 1; i
>= 0; --i
) {
2394 value_type
const par_char
= getChar(i
);
2395 if (!Encodings::isComposeChar_arabic(par_char
)) {
2396 prev_char
= par_char
;
2401 for (pos_type i
= pos
+ 1, end
= size(); i
< end
; ++i
) {
2402 value_type
const par_char
= getChar(i
);
2403 if (!Encodings::isComposeChar_arabic(par_char
)) {
2404 next_char
= par_char
;
2409 if (Encodings::is_arabic(next_char
)) {
2410 if (Encodings::is_arabic(prev_char
) &&
2411 !Encodings::is_arabic_special(prev_char
))
2412 return Encodings::transformChar(c
, Encodings::FORM_MEDIAL
);
2414 return Encodings::transformChar(c
, Encodings::FORM_INITIAL
);
2416 if (Encodings::is_arabic(prev_char
) &&
2417 !Encodings::is_arabic_special(prev_char
))
2418 return Encodings::transformChar(c
, Encodings::FORM_FINAL
);
2420 return Encodings::transformChar(c
, Encodings::FORM_ISOLATED
);
2425 int Paragraph::checkBiblio(bool track_changes
)
2428 //This is getting more and more a mess. ...We really should clean
2429 //up this bibitem issue for 1.6. See also bug 2743.
2431 // Add bibitem insets if necessary
2432 if (layout()->labeltype
!= LABEL_BIBLIO
)
2435 bool hasbibitem
= !d
->insetlist_
.empty()
2436 // Insist on it being in pos 0
2437 && getChar(0) == Paragraph::META_INSET
2438 && d
->insetlist_
.begin()->inset
->lyxCode() == BIBITEM_CODE
;
2443 // remove a bibitem in pos != 0
2444 // restore it later in pos 0 if necessary
2445 // (e.g. if a user inserts contents _before_ the item)
2446 // we're assuming there's only one of these, which there
2448 int erasedInsetPosition
= -1;
2449 InsetList::iterator it
= d
->insetlist_
.begin();
2450 InsetList::iterator end
= d
->insetlist_
.end();
2451 for (; it
!= end
; ++it
)
2452 if (it
->inset
->lyxCode() == BIBITEM_CODE
2454 InsetBibitem
* olditem
= static_cast<InsetBibitem
*>(it
->inset
);
2455 oldkey
= olditem
->getParam("key");
2456 oldlabel
= olditem
->getParam("label");
2457 erasedInsetPosition
= it
->pos
;
2458 eraseChar(erasedInsetPosition
, track_changes
);
2462 //There was an InsetBibitem at the beginning, and we didn't
2463 //have to erase one.
2464 if (hasbibitem
&& erasedInsetPosition
< 0)
2467 //There was an InsetBibitem at the beginning and we did have to
2468 //erase one. So we give its properties to the beginning inset.
2470 InsetBibitem
* inset
=
2471 static_cast<InsetBibitem
*>(d
->insetlist_
.begin()->inset
);
2472 if (!oldkey
.empty())
2473 inset
->setParam("key", oldkey
);
2474 inset
->setParam("label", oldlabel
);
2475 return -erasedInsetPosition
;
2478 //There was no inset at the beginning, so we need to create one with
2479 //the key and label of the one we erased.
2480 InsetBibitem
* inset(new InsetBibitem(InsetCommandParams(BIBITEM_CODE
)));
2481 // restore values of previously deleted item in this par.
2482 if (!oldkey
.empty())
2483 inset
->setParam("key", oldkey
);
2484 inset
->setParam("label", oldlabel
);
2485 insertInset(0, static_cast<Inset
*>(inset
),
2486 Change(track_changes
? Change::INSERTED
: Change::UNCHANGED
));
2492 void Paragraph::checkAuthors(AuthorList
const & authorList
)
2494 d
->changes_
.checkAuthors(authorList
);
2498 bool Paragraph::isUnchanged(pos_type pos
) const
2500 return lookupChange(pos
).type
== Change::UNCHANGED
;
2504 bool Paragraph::isInserted(pos_type pos
) const
2506 return lookupChange(pos
).type
== Change::INSERTED
;
2510 bool Paragraph::isDeleted(pos_type pos
) const
2512 return lookupChange(pos
).type
== Change::DELETED
;
2516 InsetList
const & Paragraph::insetList() const
2518 return d
->insetlist_
;
2522 Inset
* Paragraph::releaseInset(pos_type pos
)
2524 Inset
* inset
= d
->insetlist_
.release(pos
);
2525 /// does not honour change tracking!
2526 eraseChar(pos
, false);
2531 Inset
* Paragraph::getInset(pos_type pos
)
2533 return d
->insetlist_
.get(pos
);
2537 Inset
const * Paragraph::getInset(pos_type pos
) const
2539 return d
->insetlist_
.get(pos
);
2543 int Paragraph::numberOfOptArgs() const
2546 InsetList::const_iterator it
= insetList().begin();
2547 InsetList::const_iterator end
= insetList().end();
2548 for (; it
!= end
; ++it
) {
2549 if (it
->inset
->lyxCode() == OPTARG_CODE
)