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