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