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