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