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