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