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