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