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