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