]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
0eede1c38a00f825fe3f6c420428811488f62704
[lyx.git] / src / paragraph.C
1 /**
2  * \file paragraph.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Dekel Tsur
13  * \author Jürgen Vigna
14  *
15  * Full author contact details are available in file CREDITS.
16  */
17
18 #include <config.h>
19
20 #include "paragraph.h"
21 #include "paragraph_pimpl.h"
22
23 #include "buffer.h"
24 #include "encoding.h"
25 #include "debug.h"
26 #include "gettext.h"
27 #include "language.h"
28 #include "latexrunparams.h"
29 #include "lyxrc.h"
30 #include "lyxrow.h"
31
32 #include "insets/insetbibitem.h"
33 #include "insets/insetoptarg.h"
34
35 #include "support/lstrings.h"
36 #include "support/LAssert.h"
37 #include "support/textutils.h"
38
39 #include "support/std_sstream.h"
40
41
42 using namespace lyx::support;
43
44 using std::ostream;
45 using std::ostringstream;
46 using std::endl;
47 using std::fstream;
48 using std::ios;
49 using std::lower_bound;
50 using std::upper_bound;
51
52 using lyx::pos_type;
53
54
55 Paragraph::Paragraph()
56         : y(0), pimpl_(new Paragraph::Pimpl(this))
57 {
58         enumdepth = 0;
59         itemdepth = 0;
60         params().clear();
61 }
62
63
64 Paragraph::Paragraph(Paragraph const & lp)
65         : y(0), pimpl_(new Paragraph::Pimpl(*lp.pimpl_, this))
66 {
67         enumdepth = 0;
68         itemdepth = 0;
69         // this is because of the dummy layout of the paragraphs that
70         // follow footnotes
71         layout_ = lp.layout();
72
73         // copy everything behind the break-position to the new paragraph
74         insetlist = lp.insetlist;
75         InsetList::iterator it = insetlist.begin();
76         InsetList::iterator end = insetlist.end();
77         for (; it != end; ++it) {
78                 // currently we hold Inset*, not InsetBase*
79                 it->inset = static_cast<InsetOld*>(it->inset->clone().release());
80         }
81 }
82
83
84 void Paragraph::operator=(Paragraph const & lp)
85 {
86         // needed as we will destroy the pimpl_ before copying it
87         if (&lp != this)
88                 return;
89
90         lyxerr << "Paragraph::operator=()" << endl;
91
92         delete pimpl_;
93         pimpl_ = new Pimpl(*lp.pimpl_, this);
94
95         enumdepth = lp.enumdepth;
96         itemdepth = lp.itemdepth;
97         // this is because of the dummy layout of the paragraphs that
98         // follow footnotes
99         layout_ = lp.layout();
100
101         // copy everything behind the break-position to the new paragraph
102         insetlist = lp.insetlist;
103         InsetList::iterator it = insetlist.begin();
104         InsetList::iterator end = insetlist.end();
105         for (; it != end; ++it) {
106                 it->inset = static_cast<InsetOld*>(it->inset->clone().release());
107         }
108 }
109
110 // the destructor removes the new paragraph from the list
111 Paragraph::~Paragraph()
112 {
113         delete pimpl_;
114         //
115         //lyxerr << "Paragraph::paragraph_id = "
116         //       << Paragraph::paragraph_id << endl;
117 }
118
119
120 void Paragraph::write(Buffer const & buf, ostream & os,
121                           BufferParams const & bparams,
122                           depth_type & dth) const
123 {
124         // The beginning or end of a deeper (i.e. nested) area?
125         if (dth != params().depth()) {
126                 if (params().depth() > dth) {
127                         while (params().depth() > dth) {
128                                 os << "\n\\begin_deeper ";
129                                 ++dth;
130                         }
131                 } else {
132                         while (params().depth() < dth) {
133                                 os << "\n\\end_deeper ";
134                                 --dth;
135                         }
136                 }
137         }
138
139         // First write the layout
140         os << "\n\\begin_layout " << layout()->name() << '\n';
141
142         params().write(os);
143
144         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
145
146         Change running_change = Change(Change::UNCHANGED);
147         lyx::time_type const curtime(lyx::current_time());
148
149         int column = 0;
150         for (pos_type i = 0; i < size(); ++i) {
151                 if (!i) {
152                         os << '\n';
153                         column = 0;
154                 }
155
156                 Change change = pimpl_->lookupChangeFull(i);
157                 Changes::lyxMarkChange(os, column, curtime, running_change, change);
158                 running_change = change;
159
160                 // Write font changes
161                 LyXFont font2 = getFontSettings(bparams, i);
162                 if (font2 != font1) {
163                         font2.lyxWriteChanges(font1, os);
164                         column = 0;
165                         font1 = font2;
166                 }
167
168                 value_type const c = getChar(i);
169                 switch (c) {
170                 case META_INSET:
171                 {
172                         InsetOld const * inset = getInset(i);
173                         if (inset)
174                                 if (inset->directWrite()) {
175                                         // international char, let it write
176                                         // code directly so it's shorter in
177                                         // the file
178                                         inset->write(buf, os);
179                                 } else {
180                                         os << "\n\\begin_inset ";
181                                         inset->write(buf, os);
182                                         os << "\n\\end_inset \n\n";
183                                         column = 0;
184                                 }
185                 }
186                 break;
187                 case '\\':
188                         os << "\n\\backslash \n";
189                         column = 0;
190                         break;
191                 case '.':
192                         if (i + 1 < size() && getChar(i + 1) == ' ') {
193                                 os << ".\n";
194                                 column = 0;
195                         } else
196                                 os << '.';
197                         break;
198                 default:
199                         if ((column > 70 && c == ' ')
200                             || column > 79) {
201                                 os << '\n';
202                                 column = 0;
203                         }
204                         // this check is to amend a bug. LyX sometimes
205                         // inserts '\0' this could cause problems.
206                         if (c != '\0')
207                                 os << c;
208                         else
209                                 lyxerr << "ERROR (Paragraph::writeFile):"
210                                         " NULL char in structure." << endl;
211                         ++column;
212                         break;
213                 }
214         }
215
216         // to make reading work properly
217         if (!size()) {
218                 running_change = pimpl_->lookupChange(0);
219                 Changes::lyxMarkChange(os, column, curtime,
220                         Change(Change::UNCHANGED), running_change);
221         }
222         Changes::lyxMarkChange(os, column, curtime,
223                 running_change, Change(Change::UNCHANGED));
224
225         os << "\n\\end_layout\n";
226 }
227
228
229 void Paragraph::validate(LaTeXFeatures & features) const
230 {
231         pimpl_->validate(features, *layout());
232 }
233
234
235 void Paragraph::eraseIntern(lyx::pos_type pos)
236 {
237         pimpl_->eraseIntern(pos);
238 }
239
240
241 bool Paragraph::erase(pos_type pos)
242 {
243         return pimpl_->erase(pos);
244 }
245
246
247 int Paragraph::erase(pos_type start, pos_type end)
248 {
249         return pimpl_->erase(start, end);
250 }
251
252
253 bool Paragraph::checkInsertChar(LyXFont & font)
254 {
255         if (pimpl_->inset_owner)
256                 return pimpl_->inset_owner->checkInsertChar(font);
257         return true;
258 }
259
260
261 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
262 {
263         insertChar(pos, c, LyXFont(LyXFont::ALL_INHERIT));
264 }
265
266
267 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
268                            LyXFont const & font, Change change)
269 {
270         pimpl_->insertChar(pos, c, font, change);
271 }
272
273
274 void Paragraph::insertInset(pos_type pos, InsetOld * inset)
275 {
276         insertInset(pos, inset, LyXFont(LyXFont::ALL_INHERIT));
277 }
278
279
280 void Paragraph::insertInset(pos_type pos, InsetOld * inset,
281         LyXFont const & font, Change change)
282 {
283         pimpl_->insertInset(pos, inset, font, change);
284 }
285
286
287 bool Paragraph::insetAllowed(InsetOld::Code code)
288 {
289         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
290         if (pimpl_->inset_owner)
291                 return pimpl_->inset_owner->insetAllowed(code);
292         return true;
293 }
294
295
296 InsetOld * Paragraph::getInset(pos_type pos)
297 {
298         Assert(pos < size());
299         return insetlist.get(pos);
300 }
301
302
303 InsetOld const * Paragraph::getInset(pos_type pos) const
304 {
305         Assert(pos < size());
306         return insetlist.get(pos);
307 }
308
309
310 // Gets uninstantiated font setting at position.
311 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
312                                          pos_type pos) const
313 {
314         Assert(pos <= size());
315
316         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
317         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
318         for (; cit != end; ++cit)
319                 if (cit->pos() >= pos)
320                         break;
321
322         if (cit != end)
323                 return cit->font();
324
325         if (pos == size() && !empty())
326                 return getFontSettings(bparams, pos - 1);
327
328         return LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
329 }
330
331
332 lyx::pos_type Paragraph::getEndPosOfFontSpan(lyx::pos_type pos) const
333 {
334         Assert(pos <= size());
335
336         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
337         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
338         for (; cit != end; ++cit)
339                 if (cit->pos() >= pos)
340                         return cit->pos();
341
342         // This should not happen, but if so, we take no chances.
343         //lyxerr << "Paragraph::getEndPosOfFontSpan: This should not happen!"
344         //      << endl;
345         return pos;
346 }
347
348
349 // Gets uninstantiated font setting at position 0
350 LyXFont const Paragraph::getFirstFontSettings() const
351 {
352         if (!empty() && !pimpl_->fontlist.empty())
353                 return pimpl_->fontlist[0].font();
354
355         return LyXFont(LyXFont::ALL_INHERIT);
356 }
357
358
359 // Gets the fully instantiated font at a given position in a paragraph
360 // This is basically the same function as LyXText::GetFont() in text2.C.
361 // The difference is that this one is used for generating the LaTeX file,
362 // and thus cosmetic "improvements" are disallowed: This has to deliver
363 // the true picture of the buffer. (Asger)
364 LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
365                                  LyXFont const & outerfont) const
366 {
367         Assert(pos >= 0);
368
369         LyXLayout_ptr const & lout = layout();
370
371         pos_type const body_pos = beginningOfBody();
372
373         LyXFont layoutfont;
374         if (pos < body_pos)
375                 layoutfont = lout->labelfont;
376         else
377                 layoutfont = lout->font;
378
379         LyXFont tmpfont = getFontSettings(bparams, pos);
380         tmpfont.realize(layoutfont);
381         tmpfont.realize(outerfont);
382         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
383
384         return tmpfont;
385 }
386
387
388 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams,
389                                       LyXFont const & outerfont) const
390 {
391         LyXFont tmpfont = layout()->labelfont;
392         tmpfont.setLanguage(getParLanguage(bparams));
393         tmpfont.realize(outerfont);
394         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
395         return tmpfont;
396 }
397
398
399 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams,
400                                        LyXFont const & outerfont) const
401 {
402         LyXFont tmpfont = layout()->font;
403         tmpfont.setLanguage(getParLanguage(bparams));
404         tmpfont.realize(outerfont);
405         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
406         return tmpfont;
407 }
408
409
410 /// Returns the height of the highest font in range
411 LyXFont::FONT_SIZE
412 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
413                               LyXFont::FONT_SIZE const def_size) const
414 {
415         if (pimpl_->fontlist.empty())
416                 return def_size;
417
418         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
419         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
420         for (; end_it != end; ++end_it) {
421                 if (end_it->pos() >= endpos)
422                         break;
423         }
424
425         if (end_it != end)
426                 ++end_it;
427
428         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
429         for (; cit != end; ++cit) {
430                 if (cit->pos() >= startpos)
431                         break;
432         }
433
434         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
435         for (; cit != end_it; ++cit) {
436                 LyXFont::FONT_SIZE size = cit->font().size();
437                 if (size == LyXFont::INHERIT_SIZE)
438                         size = def_size;
439                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
440                         maxsize = size;
441         }
442         return maxsize;
443 }
444
445
446 Paragraph::value_type
447 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
448 {
449         value_type c = getChar(pos);
450         if (!lyxrc.rtl_support)
451                 return c;
452
453         value_type uc = c;
454         switch (c) {
455         case '(':
456                 uc = ')';
457                 break;
458         case ')':
459                 uc = '(';
460                 break;
461         case '[':
462                 uc = ']';
463                 break;
464         case ']':
465                 uc = '[';
466                 break;
467         case '{':
468                 uc = '}';
469                 break;
470         case '}':
471                 uc = '{';
472                 break;
473         case '<':
474                 uc = '>';
475                 break;
476         case '>':
477                 uc = '<';
478                 break;
479         }
480         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
481                 return uc;
482         else
483                 return c;
484 }
485
486
487 void Paragraph::setFont(pos_type pos, LyXFont const & font)
488 {
489         Assert(pos <= size());
490
491         // First, reduce font against layout/label font
492         // Update: The SetCharFont() routine in text2.C already
493         // reduces font, so we don't need to do that here. (Asger)
494         // No need to simplify this because it will disappear
495         // in a new kernel. (Asger)
496         // Next search font table
497
498         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
499         Pimpl::FontList::iterator it = beg;
500         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
501         for (; it != endit; ++it) {
502                 if (it->pos() >= pos)
503                         break;
504         }
505         unsigned int i = std::distance(beg, it);
506         bool notfound = (it == endit);
507
508         if (!notfound && pimpl_->fontlist[i].font() == font)
509                 return;
510
511         bool begin = pos == 0 || notfound ||
512                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
513         // Is position pos is a beginning of a font block?
514         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
515         // Is position pos is the end of a font block?
516         if (begin && end) { // A single char block
517                 if (i + 1 < pimpl_->fontlist.size() &&
518                     pimpl_->fontlist[i + 1].font() == font) {
519                         // Merge the singleton block with the next block
520                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
521                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
522                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
523                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
524                         // Merge the singleton block with the previous block
525                         pimpl_->fontlist[i - 1].pos(pos);
526                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
527                 } else
528                         pimpl_->fontlist[i].font(font);
529         } else if (begin) {
530                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
531                         pimpl_->fontlist[i - 1].pos(pos);
532                 else
533                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
534                                         Pimpl::FontTable(pos, font));
535         } else if (end) {
536                 pimpl_->fontlist[i].pos(pos - 1);
537                 if (!(i + 1 < pimpl_->fontlist.size() &&
538                       pimpl_->fontlist[i + 1].font() == font))
539                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
540                                         Pimpl::FontTable(pos, font));
541         } else { // The general case. The block is splitted into 3 blocks
542                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
543                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
544                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
545                                 Pimpl::FontTable(pos, font));
546         }
547 }
548
549
550 void Paragraph::makeSameLayout(Paragraph const & par)
551 {
552         layout(par.layout());
553         // move to pimpl?
554         params() = par.params();
555 }
556
557
558 int Paragraph::stripLeadingSpaces()
559 {
560         if (isFreeSpacing())
561                 return 0;
562
563         int i = 0;
564         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
565                 pimpl_->eraseIntern(0);
566                 ++i;
567         }
568
569         return i;
570 }
571
572
573 bool Paragraph::hasSameLayout(Paragraph const & par) const
574 {
575         return
576                 par.layout() == layout() &&
577                 params().sameLayout(par.params());
578 }
579
580
581 Paragraph::depth_type Paragraph::getDepth() const
582 {
583         return params().depth();
584 }
585
586
587 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
588 {
589         if (layout()->isEnvironment())
590                 return params().depth() + 1;
591         else
592                 return params().depth();
593 }
594
595
596 char Paragraph::getAlign() const
597 {
598         return params().align();
599 }
600
601
602 string const & Paragraph::getLabelstring() const
603 {
604         return params().labelString();
605 }
606
607
608 // the next two functions are for the manual labels
609 string const Paragraph::getLabelWidthString() const
610 {
611         if (!params().labelWidthString().empty())
612                 return params().labelWidthString();
613         else
614                 return _("Senseless with this layout!");
615 }
616
617
618 void Paragraph::setLabelWidthString(string const & s)
619 {
620         params().labelWidthString(s);
621 }
622
623
624 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
625 {
626         layout(new_layout);
627         params().labelWidthString(string());
628         params().align(LYX_ALIGN_LAYOUT);
629         params().spaceTop(VSpace(VSpace::NONE));
630         params().spaceBottom(VSpace(VSpace::NONE));
631         params().spacing(Spacing(Spacing::Default));
632 }
633
634
635 int Paragraph::beginningOfBody() const
636 {
637         if (layout()->labeltype != LABEL_MANUAL)
638                 return 0;
639
640         // Unroll the first two cycles of the loop
641         // and remember the previous character to
642         // remove unnecessary GetChar() calls
643         pos_type i = 0;
644         if (i < size() && !isNewline(i)) {
645                 ++i;
646                 char previous_char = 0;
647                 char temp = 0;
648                 if (i < size()) {
649                         previous_char = getChar(i);
650                         if (!isNewline(i)) {
651                                 ++i;
652                                 while (i < size() && previous_char != ' ') {
653                                         temp = getChar(i);
654                                         if (isNewline(i))
655                                                 break;
656
657                                         ++i;
658                                         previous_char = temp;
659                                 }
660                         }
661                 }
662         }
663
664         return i;
665 }
666
667
668 // returns -1 if inset not found
669 int Paragraph::getPositionOfInset(InsetOld const * inset) const
670 {
671         // Find the entry.
672         InsetList::const_iterator it = insetlist.begin();
673         InsetList::const_iterator end = insetlist.end();
674         for (; it != end; ++it)
675                 if (it->inset == inset)
676                         return it->pos;
677         return -1;
678 }
679
680
681 InsetBibitem * Paragraph::bibitem() const
682 {
683         InsetList::const_iterator it = insetlist.begin();
684         if (it != insetlist.end() && it->inset->lyxCode() == InsetOld::BIBTEX_CODE)
685                 return static_cast<InsetBibitem *>(it->inset);
686         return 0;
687 }
688
689
690
691 // This could go to ParagraphParameters if we want to
692 int Paragraph::startTeXParParams(BufferParams const & bparams,
693                                  ostream & os, bool moving_arg) const
694 {
695         int column = 0;
696
697         if (params().noindent()) {
698                 os << "\\noindent ";
699                 column += 10;
700         }
701
702         switch (params().align()) {
703         case LYX_ALIGN_NONE:
704         case LYX_ALIGN_BLOCK:
705         case LYX_ALIGN_LAYOUT:
706         case LYX_ALIGN_SPECIAL:
707                 break;
708         case LYX_ALIGN_LEFT:
709         case LYX_ALIGN_RIGHT:
710         case LYX_ALIGN_CENTER:
711                 if (moving_arg) {
712                         os << "\\protect";
713                         column = 8;
714                 }
715                 break;
716         }
717
718         switch (params().align()) {
719         case LYX_ALIGN_NONE:
720         case LYX_ALIGN_BLOCK:
721         case LYX_ALIGN_LAYOUT:
722         case LYX_ALIGN_SPECIAL:
723                 break;
724         case LYX_ALIGN_LEFT:
725                 if (getParLanguage(bparams)->babel() != "hebrew") {
726                         os << "\\begin{flushleft}";
727                         column += 17;
728                 } else {
729                         os << "\\begin{flushright}";
730                         column += 18;
731                 }
732                 break;
733         case LYX_ALIGN_RIGHT:
734                 if (getParLanguage(bparams)->babel() != "hebrew") {
735                         os << "\\begin{flushright}";
736                         column += 18;
737                 } else {
738                         os << "\\begin{flushleft}";
739                         column += 17;
740                 }
741                 break;
742         case LYX_ALIGN_CENTER:
743                 os << "\\begin{center}";
744                 column += 14;
745                 break;
746         }
747
748         return column;
749 }
750
751
752 // This could go to ParagraphParameters if we want to
753 int Paragraph::endTeXParParams(BufferParams const & bparams,
754                                ostream & os, bool moving_arg) const
755 {
756         int column = 0;
757
758         switch (params().align()) {
759         case LYX_ALIGN_NONE:
760         case LYX_ALIGN_BLOCK:
761         case LYX_ALIGN_LAYOUT:
762         case LYX_ALIGN_SPECIAL:
763                 break;
764         case LYX_ALIGN_LEFT:
765         case LYX_ALIGN_RIGHT:
766         case LYX_ALIGN_CENTER:
767                 if (moving_arg) {
768                         os << "\\protect";
769                         column = 8;
770                 }
771                 break;
772         }
773
774         switch (params().align()) {
775         case LYX_ALIGN_NONE:
776         case LYX_ALIGN_BLOCK:
777         case LYX_ALIGN_LAYOUT:
778         case LYX_ALIGN_SPECIAL:
779                 break;
780         case LYX_ALIGN_LEFT:
781                 if (getParLanguage(bparams)->babel() != "hebrew") {
782                         os << "\\end{flushleft}";
783                         column = 15;
784                 } else {
785                         os << "\\end{flushright}";
786                         column = 16;
787                 }
788                 break;
789         case LYX_ALIGN_RIGHT:
790                 if (getParLanguage(bparams)->babel() != "hebrew") {
791                         os << "\\end{flushright}";
792                         column+= 16;
793                 } else {
794                         os << "\\end{flushleft}";
795                         column = 15;
796                 }
797                 break;
798         case LYX_ALIGN_CENTER:
799                 os << "\\end{center}";
800                 column = 12;
801                 break;
802         }
803         return column;
804 }
805
806
807 // This one spits out the text of the paragraph
808 bool Paragraph::simpleTeXOnePar(Buffer const & buf,
809                                 BufferParams const & bparams,
810                                 LyXFont const & outerfont,
811                                 ostream & os, TexRow & texrow,
812                                 LatexRunParams const & runparams)
813 {
814         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
815
816         bool return_value = false;
817
818         LyXLayout_ptr style;
819
820         // well we have to check if we are in an inset with unlimited
821         // lenght (all in one row) if that is true then we don't allow
822         // any special options in the paragraph and also we don't allow
823         // any environment other then "Standard" to be valid!
824         bool asdefault =
825                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
826
827         if (asdefault) {
828                 style = bparams.getLyXTextClass().defaultLayout();
829         } else {
830                 style = layout();
831         }
832
833         LyXFont basefont;
834
835         // Maybe we have to create a optional argument.
836         pos_type body_pos;
837
838         // FIXME: can we actually skip this check and just call
839         // beginningOfBody() ??
840         if (style->labeltype != LABEL_MANUAL) {
841                 body_pos = 0;
842         } else {
843                 body_pos = beginningOfBody();
844         }
845
846         unsigned int column = 0;
847
848         if (body_pos > 0) {
849                 os << '[';
850                 ++column;
851                 basefont = getLabelFont(bparams, outerfont);
852         } else {
853                 basefont = getLayoutFont(bparams, outerfont);
854         }
855
856         bool moving_arg = runparams.moving_arg;
857         moving_arg |= style->needprotect;
858
859         // Which font is currently active?
860         LyXFont running_font(basefont);
861         // Do we have an open font change?
862         bool open_font = false;
863
864         Change::Type running_change = Change::UNCHANGED;
865
866         texrow.start(id(), 0);
867
868         // if the paragraph is empty, the loop will not be entered at all
869         if (empty()) {
870                 if (style->isCommand()) {
871                         os << '{';
872                         ++column;
873                 }
874                 if (!asdefault)
875                         column += startTeXParParams(bparams, os, moving_arg);
876
877         }
878
879         for (pos_type i = 0; i < size(); ++i) {
880                 ++column;
881                 // First char in paragraph or after label?
882                 if (i == body_pos) {
883                         if (body_pos > 0) {
884                                 if (open_font) {
885                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
886                                         open_font = false;
887                                 }
888                                 basefont = getLayoutFont(bparams, outerfont);
889                                 running_font = basefont;
890                                 os << ']';
891                                 ++column;
892                         }
893                         if (style->isCommand()) {
894                                 os << '{';
895                                 ++column;
896                         }
897
898                         if (!asdefault)
899                                 column += startTeXParParams(bparams, os,
900                                                             moving_arg);
901                 }
902
903                 value_type c = getChar(i);
904
905                 // Fully instantiated font
906                 LyXFont font = getFont(bparams, i, outerfont);
907
908                 LyXFont const last_font = running_font;
909
910                 // Spaces at end of font change are simulated to be
911                 // outside font change, i.e. we write "\textXX{text} "
912                 // rather than "\textXX{text }". (Asger)
913                 if (open_font && c == ' ' && i <= size() - 2) {
914                         LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
915                         if (next_font != running_font
916                             && next_font != font) {
917                                 font = next_font;
918                         }
919                 }
920
921                 // We end font definition before blanks
922                 if (open_font &&
923                     (font != running_font ||
924                      font.language() != running_font.language()))
925                 {
926                         column += running_font.latexWriteEndChanges(os,
927                                                                     basefont,
928                                                                     (i == body_pos-1) ? basefont : font);
929                         running_font = basefont;
930                         open_font = false;
931                 }
932
933                 // Blanks are printed before start of fontswitch
934                 if (c == ' ') {
935                         // Do not print the separation of the optional argument
936                         if (i != body_pos - 1) {
937                                 pimpl_->simpleTeXBlanks(os, texrow, i,
938                                                        column, font, *style);
939                         }
940                 }
941
942                 // Do we need to change font?
943                 if ((font != running_font ||
944                      font.language() != running_font.language()) &&
945                         i != body_pos - 1)
946                 {
947                         column += font.latexWriteStartChanges(os, basefont,
948                                                               last_font);
949                         running_font = font;
950                         open_font = true;
951                 }
952
953                 Change::Type change = pimpl_->lookupChange(i);
954
955                 column += Changes::latexMarkChange(os, running_change, change);
956                 running_change = change;
957
958                 LatexRunParams rp = runparams;
959                 rp.moving_arg = moving_arg;
960                 rp.free_spacing = style->free_spacing;
961                 pimpl_->simpleTeXSpecialChars(buf, bparams,
962                                               os, texrow, runparams,
963                                               font, running_font,
964                                               basefont, outerfont, open_font,
965                                               running_change,
966                                               *style, i, column, c);
967         }
968
969         column += Changes::latexMarkChange(os,
970                         running_change, Change::UNCHANGED);
971
972         // If we have an open font definition, we have to close it
973         if (open_font) {
974 #ifdef FIXED_LANGUAGE_END_DETECTION
975                 if (next_) {
976                         running_font
977                                 .latexWriteEndChanges(os, basefont,
978                                                       next_->getFont(bparams,
979                                                       0, outerfont));
980                 } else {
981                         running_font.latexWriteEndChanges(os, basefont,
982                                                           basefont);
983                 }
984 #else
985 #ifdef WITH_WARNINGS
986 //#warning For now we ALWAYS have to close the foreign font settings if they are
987 //#warning there as we start another \selectlanguage with the next paragraph if
988 //#warning we are in need of this. This should be fixed sometime (Jug)
989 #endif
990                 running_font.latexWriteEndChanges(os, basefont,  basefont);
991 #endif
992         }
993
994         // Needed if there is an optional argument but no contents.
995         if (body_pos > 0 && body_pos == size()) {
996                 os << "]~";
997                 return_value = false;
998         }
999
1000         if (!asdefault) {
1001                 column += endTeXParParams(bparams, os, moving_arg);
1002         }
1003
1004         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1005         return return_value;
1006 }
1007
1008
1009
1010
1011 bool Paragraph::isHfill(pos_type pos) const
1012 {
1013         return IsInsetChar(getChar(pos))
1014                && getInset(pos)->lyxCode() == InsetOld::HFILL_CODE;
1015 }
1016
1017
1018 bool Paragraph::isInset(pos_type pos) const
1019 {
1020         return IsInsetChar(getChar(pos));
1021 }
1022
1023
1024 bool Paragraph::isNewline(pos_type pos) const
1025 {
1026         return IsInsetChar(getChar(pos))
1027                && getInset(pos)->lyxCode() == InsetOld::NEWLINE_CODE;
1028 }
1029
1030
1031 bool Paragraph::isSeparator(pos_type pos) const
1032 {
1033         return IsSeparatorChar(getChar(pos));
1034 }
1035
1036
1037 bool Paragraph::isLineSeparator(pos_type pos) const
1038 {
1039         value_type const c = getChar(pos);
1040         return IsLineSeparatorChar(c)
1041                 || (IsInsetChar(c) && getInset(pos) &&
1042                 getInset(pos)->isLineSeparator());
1043 }
1044
1045
1046 bool Paragraph::isKomma(pos_type pos) const
1047 {
1048         return IsKommaChar(getChar(pos));
1049 }
1050
1051
1052 /// Used by the spellchecker
1053 bool Paragraph::isLetter(pos_type pos) const
1054 {
1055         value_type const c = getChar(pos);
1056         if (IsLetterChar(c))
1057                 return true;
1058         if (isInset(pos))
1059                 return getInset(pos)->isLetter();
1060         // We want to pass the ' and escape chars to ispell
1061         string const extra = lyxrc.isp_esc_chars + '\'';
1062         return contains(extra, c);
1063 }
1064
1065
1066 bool Paragraph::isWord(pos_type pos) const
1067 {
1068         return IsWordChar(getChar(pos)) ;
1069 }
1070
1071
1072 Language const *
1073 Paragraph::getParLanguage(BufferParams const & bparams) const
1074 {
1075         if (!empty())
1076                 return getFirstFontSettings().language();
1077 #warning FIXME we should check the prev par as well (Lgb)
1078         return bparams.language;
1079 }
1080
1081
1082 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1083 {
1084         return lyxrc.rtl_support
1085                 && getParLanguage(bparams)->RightToLeft()
1086                 && !(inInset() && inInset()->owner() &&
1087                      inInset()->owner()->lyxCode() == InsetOld::ERT_CODE);
1088 }
1089
1090
1091 void Paragraph::changeLanguage(BufferParams const & bparams,
1092                                Language const * from, Language const * to)
1093 {
1094         for (pos_type i = 0; i < size(); ++i) {
1095                 LyXFont font = getFontSettings(bparams, i);
1096                 if (font.language() == from) {
1097                         font.setLanguage(to);
1098                         setFont(i, font);
1099                 }
1100         }
1101 }
1102
1103
1104 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1105 {
1106         Language const * doc_language = bparams.language;
1107         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1108         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1109
1110         for (; cit != end; ++cit)
1111                 if (cit->font().language() != ignore_language &&
1112                     cit->font().language() != latex_language &&
1113                     cit->font().language() != doc_language)
1114                         return true;
1115         return false;
1116 }
1117
1118
1119 // Convert the paragraph to a string.
1120 // Used for building the table of contents
1121 string const Paragraph::asString(Buffer const & buffer, bool label) const
1122 {
1123 #if 0
1124         string s;
1125         if (label && !params().labelString().empty())
1126                 s += params().labelString() + ' ';
1127
1128         for (pos_type i = 0; i < size(); ++i) {
1129                 value_type c = getChar(i);
1130                 if (IsPrintable(c))
1131                         s += c;
1132                 else if (c == META_INSET &&
1133                          getInset(i)->lyxCode() == InsetOld::MATH_CODE) {
1134                         ostringstream ost;
1135                         getInset(i)->ascii(buffer, ost);
1136                         s += subst(STRCONV(ost.str()),'\n',' ');
1137                 }
1138         }
1139
1140         return s;
1141 #else
1142         // This should really be done by the caller and not here.
1143         string ret(asString(buffer, 0, size(), label));
1144         return subst(ret, '\n', ' ');
1145 #endif
1146 }
1147
1148
1149 string const Paragraph::asString(Buffer const & buffer,
1150                                  pos_type beg, pos_type end, bool label) const
1151 {
1152         ostringstream os;
1153
1154         if (beg == 0 && label && !params().labelString().empty())
1155                 os << params().labelString() << ' ';
1156
1157         for (pos_type i = beg; i < end; ++i) {
1158                 value_type const c = getUChar(buffer.params, i);
1159                 if (IsPrintable(c))
1160                         os << c;
1161                 else if (c == META_INSET)
1162                         getInset(i)->ascii(buffer, os);
1163         }
1164
1165         return STRCONV(os.str());
1166 }
1167
1168
1169 void Paragraph::setInsetOwner(UpdatableInset * inset)
1170 {
1171         pimpl_->inset_owner = inset;
1172         InsetList::iterator it = insetlist.begin();
1173         InsetList::iterator end = insetlist.end();
1174         for (; it != end; ++it)
1175                 if (it->inset)
1176                         it->inset->setOwner(inset);
1177 }
1178
1179
1180 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1181 {
1182         insetlist.deleteInsetsLyXText(bv);
1183 }
1184
1185
1186 void Paragraph::setContentsFromPar(Paragraph const & par)
1187 {
1188         pimpl_->setContentsFromPar(par);
1189 }
1190
1191
1192 void Paragraph::trackChanges(Change::Type type)
1193 {
1194         pimpl_->trackChanges(type);
1195 }
1196
1197
1198 void Paragraph::untrackChanges()
1199 {
1200         pimpl_->untrackChanges();
1201 }
1202
1203
1204 void Paragraph::cleanChanges()
1205 {
1206         pimpl_->cleanChanges();
1207 }
1208
1209
1210 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1211 {
1212         Assert(!size() || pos < size());
1213         return pimpl_->lookupChange(pos);
1214 }
1215
1216
1217 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1218 {
1219         Assert(!size() || pos < size());
1220         return pimpl_->lookupChangeFull(pos);
1221 }
1222
1223
1224 bool Paragraph::isChanged(pos_type start, pos_type end) const
1225 {
1226         return pimpl_->isChanged(start, end);
1227 }
1228
1229
1230 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1231 {
1232         return pimpl_->isChangeEdited(start, end);
1233 }
1234
1235
1236 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1237 {
1238         pimpl_->setChange(pos, type);
1239
1240 }
1241
1242
1243 void Paragraph::markErased()
1244 {
1245         pimpl_->markErased();
1246 }
1247
1248
1249 void Paragraph::acceptChange(pos_type start, pos_type end)
1250 {
1251         return pimpl_->acceptChange(start, end);
1252 }
1253
1254
1255 void Paragraph::rejectChange(pos_type start, pos_type end)
1256 {
1257         return pimpl_->rejectChange(start, end);
1258 }
1259
1260
1261 lyx::pos_type Paragraph::size() const
1262 {
1263         return pimpl_->size();
1264 }
1265
1266
1267 bool Paragraph::empty() const
1268 {
1269         return pimpl_->empty();
1270 }
1271
1272
1273 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1274 {
1275         return pimpl_->getChar(pos);
1276 }
1277
1278
1279 int Paragraph::id() const
1280 {
1281         return pimpl_->id_;
1282 }
1283
1284
1285 void Paragraph::id(int i)
1286 {
1287         pimpl_->id_ = i;
1288 }
1289
1290
1291 LyXLayout_ptr const & Paragraph::layout() const
1292 {
1293 /*
1294         InsetOld * inset = inInset();
1295         if (inset && inset->lyxCode() == InsetOld::ENVIRONMENT_CODE)
1296                 return static_cast<InsetEnvironment*>(inset)->layout();
1297 */
1298         return layout_;
1299 }
1300
1301
1302 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1303 {
1304         layout_ = new_layout;
1305 }
1306
1307
1308 UpdatableInset * Paragraph::inInset() const
1309 {
1310         return pimpl_->inset_owner;
1311 }
1312
1313
1314 void Paragraph::clearContents()
1315 {
1316         pimpl_->clear();
1317 }
1318
1319 void Paragraph::setChar(pos_type pos, value_type c)
1320 {
1321         pimpl_->setChar(pos, c);
1322 }
1323
1324
1325 ParagraphParameters & Paragraph::params()
1326 {
1327         return pimpl_->params;
1328 }
1329
1330
1331 ParagraphParameters const & Paragraph::params() const
1332 {
1333         return pimpl_->params;
1334 }
1335
1336
1337 bool Paragraph::isFreeSpacing() const
1338 {
1339         if (layout()->free_spacing)
1340                 return true;
1341
1342         // for now we just need this, later should we need this in some
1343         // other way we can always add a function to InsetOld too.
1344         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1345                 return pimpl_->inset_owner->owner()->lyxCode() == InsetOld::ERT_CODE;
1346         return false;
1347 }
1348
1349
1350 bool Paragraph::allowEmpty() const
1351 {
1352         if (layout()->keepempty)
1353                 return true;
1354         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1355                 return pimpl_->inset_owner->owner()->lyxCode() == InsetOld::ERT_CODE;
1356         return false;
1357 }