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