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