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