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