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