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