]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
The "I want this in now" patch.
[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                                 bool moving_arg)
920 {
921         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
922
923         bool return_value = false;
924
925         LyXLayout_ptr style;
926
927         // well we have to check if we are in an inset with unlimited
928         // lenght (all in one row) if that is true then we don't allow
929         // any special options in the paragraph and also we don't allow
930         // any environment other then "Standard" to be valid!
931         bool asdefault =
932                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
933
934         if (asdefault) {
935                 style = bparams.getLyXTextClass().defaultLayout();
936         } else {
937                 style = layout();
938         }
939
940         LyXFont basefont;
941
942         // Maybe we have to create a optional argument.
943         pos_type body_pos;
944
945         // FIXME: can we actually skip this check and just call
946         // beginningOfBody() ??
947         if (style->labeltype != LABEL_MANUAL) {
948                 body_pos = 0;
949         } else {
950                 body_pos = beginningOfBody();
951         }
952
953         unsigned int column = 0;
954
955         if (body_pos > 0) {
956                 os << '[';
957                 ++column;
958                 basefont = getLabelFont(bparams, outerfont);
959         } else {
960                 basefont = getLayoutFont(bparams, outerfont);
961         }
962
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.fragile = moving_arg;
1066                 pimpl_->simpleTeXSpecialChars(buf, bparams,
1067                                               os, texrow, runparams,
1068                                               font, running_font,
1069                                               basefont, outerfont, open_font,
1070                                               running_change,
1071                                               *style, i, column, c);
1072         }
1073
1074         column += Changes::latexMarkChange(os,
1075                         running_change, Change::UNCHANGED);
1076
1077         // If we have an open font definition, we have to close it
1078         if (open_font) {
1079 #ifdef FIXED_LANGUAGE_END_DETECTION
1080                 if (next_) {
1081                         running_font
1082                                 .latexWriteEndChanges(os, basefont,
1083                                                       next_->getFont(bparams,
1084                                                       0, outerfont));
1085                 } else {
1086                         running_font.latexWriteEndChanges(os, basefont,
1087                                                           basefont);
1088                 }
1089 #else
1090 #ifdef WITH_WARNINGS
1091 //#warning For now we ALWAYS have to close the foreign font settings if they are
1092 //#warning there as we start another \selectlanguage with the next paragraph if
1093 //#warning we are in need of this. This should be fixed sometime (Jug)
1094 #endif
1095                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1096 #endif
1097         }
1098
1099         // Needed if there is an optional argument but no contents.
1100         if (body_pos > 0 && body_pos == size()) {
1101                 os << "]~";
1102                 return_value = false;
1103         }
1104
1105         if (!asdefault) {
1106                 column += endTeXParParams(bparams, os, moving_arg);
1107         }
1108
1109         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1110         return return_value;
1111 }
1112
1113
1114
1115
1116 bool Paragraph::isHfill(pos_type pos) const
1117 {
1118         return IsInsetChar(getChar(pos))
1119                && getInset(pos)->lyxCode() == Inset::HFILL_CODE;
1120 }
1121
1122
1123 bool Paragraph::isInset(pos_type pos) const
1124 {
1125         return IsInsetChar(getChar(pos));
1126 }
1127
1128
1129 bool Paragraph::isNewline(pos_type pos) const
1130 {
1131         return IsInsetChar(getChar(pos))
1132                && getInset(pos)->lyxCode() == Inset::NEWLINE_CODE;
1133 }
1134
1135
1136 bool Paragraph::isSeparator(pos_type pos) const
1137 {
1138         return IsSeparatorChar(getChar(pos));
1139 }
1140
1141
1142 bool Paragraph::isLineSeparator(pos_type pos) const
1143 {
1144         value_type const c = getChar(pos);
1145         return IsLineSeparatorChar(c)
1146                 || (IsInsetChar(c) && getInset(pos) &&
1147                 getInset(pos)->isLineSeparator());
1148 }
1149
1150
1151 bool Paragraph::isKomma(pos_type pos) const
1152 {
1153         return IsKommaChar(getChar(pos));
1154 }
1155
1156
1157 /// Used by the spellchecker
1158 bool Paragraph::isLetter(pos_type pos) const
1159 {
1160         value_type const c = getChar(pos);
1161         if (IsLetterChar(c))
1162                 return true;
1163         if (isInset(pos))
1164                 return getInset(pos)->isLetter();
1165         // We want to pass the ' and escape chars to ispell
1166         string const extra = lyxrc.isp_esc_chars + '\'';
1167         return contains(extra, c);
1168 }
1169
1170
1171 bool Paragraph::isWord(pos_type pos) const
1172 {
1173         return IsWordChar(getChar(pos)) ;
1174 }
1175
1176
1177 Language const *
1178 Paragraph::getParLanguage(BufferParams const & bparams) const
1179 {
1180         if (!empty()) {
1181                 return getFirstFontSettings().language();
1182 #warning FIXME we should check the prev par as well (Lgb)
1183 #if 0
1184         } else if (previous_) {
1185                 return previous_->getParLanguage(bparams);
1186 #endif
1187         }else
1188                 return bparams.language;
1189 }
1190
1191
1192 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1193 {
1194         return lyxrc.rtl_support
1195                 && getParLanguage(bparams)->RightToLeft()
1196                 && !(inInset() && inInset()->owner() &&
1197                      inInset()->owner()->lyxCode() == Inset::ERT_CODE);
1198 }
1199
1200
1201 void Paragraph::changeLanguage(BufferParams const & bparams,
1202                                Language const * from, Language const * to)
1203 {
1204         for (pos_type i = 0; i < size(); ++i) {
1205                 LyXFont font = getFontSettings(bparams, i);
1206                 if (font.language() == from) {
1207                         font.setLanguage(to);
1208                         setFont(i, font);
1209                 }
1210         }
1211 }
1212
1213
1214 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1215 {
1216         Language const * doc_language = bparams.language;
1217         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1218         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1219
1220         for (; cit != end; ++cit)
1221                 if (cit->font().language() != ignore_language &&
1222                     cit->font().language() != latex_language &&
1223                         cit->font().language() != doc_language)
1224                         return true;
1225         return false;
1226 }
1227
1228
1229 // Convert the paragraph to a string.
1230 // Used for building the table of contents
1231 string const Paragraph::asString(Buffer const * buffer, bool label) const
1232 {
1233         string s;
1234         if (label && !params().labelString().empty())
1235                 s += params().labelString() + ' ';
1236
1237         for (pos_type i = 0; i < size(); ++i) {
1238                 value_type c = getChar(i);
1239                 if (IsPrintable(c))
1240                         s += c;
1241                 else if (c == META_INSET &&
1242                          getInset(i)->lyxCode() == Inset::MATH_CODE) {
1243                         ostringstream ost;
1244                         getInset(i)->ascii(buffer, ost);
1245                         s += subst(STRCONV(ost.str()),'\n',' ');
1246                 }
1247         }
1248
1249         return s;
1250 }
1251
1252
1253 string const Paragraph::asString(Buffer const * buffer,
1254                                  pos_type beg, pos_type end, bool label) const
1255 {
1256         ostringstream os;
1257
1258         if (beg == 0 && label && !params().labelString().empty())
1259                 os << params().labelString() << ' ';
1260
1261         for (pos_type i = beg; i < end; ++i) {
1262                 value_type const c = getUChar(buffer->params, i);
1263                 if (IsPrintable(c))
1264                         os << c;
1265                 else if (c == META_INSET)
1266                         getInset(i)->ascii(buffer, os);
1267         }
1268
1269         return STRCONV(os.str());
1270 }
1271
1272
1273 void Paragraph::setInsetOwner(Inset * i)
1274 {
1275         pimpl_->inset_owner = i;
1276         InsetList::iterator it = insetlist.begin();
1277         InsetList::iterator end = insetlist.end();
1278         for (; it != end; ++it)
1279                 if (it.getInset())
1280                         it.getInset()->setOwner(i);
1281 }
1282
1283
1284 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1285 {
1286         // then the insets
1287         insetlist.deleteInsetsLyXText(bv);
1288 }
1289
1290
1291 void Paragraph::resizeInsetsLyXText(BufferView * bv)
1292 {
1293         // then the insets
1294         insetlist.resizeInsetsLyXText(bv);
1295 }
1296
1297
1298 void Paragraph::setContentsFromPar(Paragraph const & par)
1299 {
1300         pimpl_->setContentsFromPar(par);
1301 }
1302
1303
1304 void Paragraph::trackChanges(Change::Type type)
1305 {
1306         pimpl_->trackChanges(type);
1307 }
1308
1309
1310 void Paragraph::untrackChanges()
1311 {
1312         pimpl_->untrackChanges();
1313 }
1314
1315
1316 void Paragraph::cleanChanges()
1317 {
1318         pimpl_->cleanChanges();
1319 }
1320
1321
1322 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1323 {
1324         lyx::Assert(!size() || pos < size());
1325         return pimpl_->lookupChange(pos);
1326 }
1327
1328
1329 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1330 {
1331         lyx::Assert(!size() || pos < size());
1332         return pimpl_->lookupChangeFull(pos);
1333 }
1334
1335
1336 bool Paragraph::isChanged(pos_type start, pos_type end) const
1337 {
1338         return pimpl_->isChanged(start, end);
1339 }
1340
1341
1342 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1343 {
1344         return pimpl_->isChangeEdited(start, end);
1345 }
1346
1347
1348 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1349 {
1350         pimpl_->setChange(pos, type);
1351
1352 }
1353
1354
1355 void Paragraph::markErased()
1356 {
1357         pimpl_->markErased();
1358 }
1359
1360
1361 void Paragraph::acceptChange(pos_type start, pos_type end)
1362 {
1363         return pimpl_->acceptChange(start, end);
1364 }
1365
1366
1367 void Paragraph::rejectChange(pos_type start, pos_type end)
1368 {
1369         return pimpl_->rejectChange(start, end);
1370 }
1371
1372
1373 lyx::pos_type Paragraph::size() const
1374 {
1375         return pimpl_->size();
1376 }
1377
1378
1379 bool Paragraph::empty() const
1380 {
1381         return pimpl_->empty();
1382 }
1383
1384
1385 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1386 {
1387         return pimpl_->getChar(pos);
1388 }
1389
1390
1391 int Paragraph::id() const
1392 {
1393         return pimpl_->id_;
1394 }
1395
1396
1397 LyXLayout_ptr const & Paragraph::layout() const
1398 {
1399         Inset * inset = inInset();
1400         if (inset && inset->lyxCode() == Inset::ENVIRONMENT_CODE)
1401                 return static_cast<InsetEnvironment*>(inset)->layout();
1402         return layout_;
1403 }
1404
1405
1406 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1407 {
1408         layout_ = new_layout;
1409 }
1410
1411
1412 Inset * Paragraph::inInset() const
1413 {
1414         return pimpl_->inset_owner;
1415 }
1416
1417
1418 void Paragraph::clearContents()
1419 {
1420         pimpl_->clear();
1421 }
1422
1423 void Paragraph::setChar(pos_type pos, value_type c)
1424 {
1425         pimpl_->setChar(pos, c);
1426 }
1427
1428
1429 ParagraphParameters & Paragraph::params()
1430 {
1431         return pimpl_->params;
1432 }
1433
1434
1435 ParagraphParameters const & Paragraph::params() const
1436 {
1437         return pimpl_->params;
1438 }
1439
1440
1441 bool Paragraph::isFreeSpacing() const
1442 {
1443         // for now we just need this, later should we need this in some
1444         // other way we can always add a function to Inset::() too.
1445         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1446                 return (pimpl_->inset_owner->owner()->lyxCode() == Inset::ERT_CODE);
1447         return false;
1448 }