]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
use exclicit temp var
[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 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include "paragraph.h"
18 #include "paragraph_pimpl.h"
19 #include "lyxrc.h"
20 #include "layout.h"
21 #include "language.h"
22 #include "tex-strings.h"
23 #include "buffer.h"
24 #include "bufferparams.h"
25 #include "debug.h"
26 #include "LaTeXFeatures.h"
27 #include "texrow.h"
28 #include "BufferView.h"
29 #include "encoding.h"
30 #include "ParameterStruct.h"
31 #include "gettext.h"
32 #include "lyxtextclasslist.h"
33
34 #include "insets/insetinclude.h"
35 #include "insets/insetbib.h"
36 #include "insets/insettext.h"
37
38 #include "support/filetools.h"
39 #include "support/lstrings.h"
40 #include "support/lyxmanip.h"
41 #include "support/FileInfo.h"
42 #include "support/LAssert.h"
43 #include "support/textutils.h"
44
45 #include <algorithm>
46 #include <fstream>
47 #include <csignal>
48
49 using std::ostream;
50 using std::endl;
51 using std::fstream;
52 using std::ios;
53 using std::lower_bound;
54 using std::upper_bound;
55 using std::reverse;
56
57 using lyx::pos_type;
58 using lyx::layout_type;
59
60 int tex_code_break_column = 72;  // needs non-zero initialization. set later.
61 // this is a bad idea, but how can Paragraph find its buffer to get
62 // parameters? (JMarc)
63
64 extern string bibitemWidest(Buffer const *);
65
66 // this is a minibuffer
67
68 namespace {
69
70 char minibuffer_char;
71 LyXFont minibuffer_font;
72 Inset * minibuffer_inset;
73
74 } // namespace anon
75
76
77 extern BufferView * current_view;
78
79
80 Paragraph::Paragraph()
81         : layout(0), pimpl_(new Paragraph::Pimpl(this))
82 {
83         for (int i = 0; i < 10; ++i)
84                 setCounter(i, 0);
85         next_ = 0;
86         previous_ = 0;
87         enumdepth = 0;
88         itemdepth = 0;
89         bibkey = 0; // ale970302
90         clear();
91 }
92
93
94 // This constructor inserts the new paragraph in a list.
95 Paragraph::Paragraph(Paragraph * par)
96         : layout(0), pimpl_(new Paragraph::Pimpl(this))
97 {
98         for (int i = 0; i < 10; ++i)
99                 setCounter(i, 0);
100         enumdepth = 0;
101         itemdepth = 0;
102
103         // double linked list begin
104         next_ = par->next_;
105         if (next_)
106                 next_->previous_ = this;
107         previous_ = par;
108         previous_->next_ = this;
109         // end
110
111         bibkey = 0; // ale970302        
112
113         clear();
114 }
115
116
117 Paragraph::Paragraph(Paragraph const & lp, bool same_ids)
118         : layout(0), pimpl_(new Paragraph::Pimpl(*lp.pimpl_, this, same_ids))
119 {
120         for (int i = 0; i < 10; ++i)
121                 setCounter(i, 0);
122         enumdepth = 0;
123         itemdepth = 0;
124         next_     = 0;
125         previous_ = 0;
126
127         // this is because of the dummy layout of the paragraphs that
128         // follow footnotes
129         layout = lp.layout;
130
131         // ale970302
132         if (lp.bibkey) {
133                 bibkey = static_cast<InsetBibKey *>
134                         (lp.bibkey->clone(*current_view->buffer()));
135         } else {
136                 bibkey = 0;
137         }
138         
139         // copy everything behind the break-position to the new paragraph
140         insetlist = lp.insetlist;
141         for (InsetList::iterator it = insetlist.begin();
142              it != insetlist.end(); ++it)
143         {
144                 it->inset = it->inset->clone(*current_view->buffer(), same_ids);
145                 // tell the new inset who is the boss now
146                 it->inset->parOwner(this);
147         }
148 }
149
150
151 // the destructor removes the new paragraph from the list
152 Paragraph::~Paragraph()
153 {
154         if (previous_)
155                 previous_->next_ = next_;
156         if (next_)
157                 next_->previous_ = previous_;
158
159         for (InsetList::iterator it = insetlist.begin();
160              it != insetlist.end(); ++it) {
161                 delete it->inset;
162         }
163
164         // ale970302
165         delete bibkey;
166
167         delete pimpl_;
168         //
169         //lyxerr << "Paragraph::paragraph_id = "
170         //       << Paragraph::paragraph_id << endl;
171 }
172
173
174 void Paragraph::writeFile(Buffer const * buf, ostream & os,
175                           BufferParams const & bparams,
176                           depth_type dth) const
177 {
178         // The beginning or end of a deeper (i.e. nested) area?
179         if (dth != params().depth()) {
180                 if (params().depth() > dth) {
181                         while (params().depth() > dth) {
182                                 os << "\n\\begin_deeper ";
183                                 ++dth;
184                         }
185                 } else {
186                         while (params().depth() < dth) {
187                                 os << "\n\\end_deeper ";
188                                 --dth;
189                         }
190                 }
191         }
192         
193         // First write the layout
194         os << "\n\\layout "
195            << textclasslist.NameOfLayout(bparams.textclass, layout)
196            << "\n";
197         
198         // Maybe some vertical spaces.
199         if (params().spaceTop().kind() != VSpace::NONE)
200                 os << "\\added_space_top "
201                    << params().spaceTop().asLyXCommand() << " ";
202         if (params().spaceBottom().kind() != VSpace::NONE)
203                 os << "\\added_space_bottom "
204                    << params().spaceBottom().asLyXCommand() << " ";
205         
206         // Maybe the paragraph has special spacing
207         params().spacing().writeFile(os, true);
208         
209         // The labelwidth string used in lists.
210         if (!params().labelWidthString().empty())
211                 os << "\\labelwidthstring "
212                    << params().labelWidthString() << '\n';
213         
214         // Lines above or below?
215         if (params().lineTop())
216                 os << "\\line_top ";
217         if (params().lineBottom())
218                 os << "\\line_bottom ";
219         
220         // Pagebreaks above or below?
221         if (params().pagebreakTop())
222                 os << "\\pagebreak_top ";
223         if (params().pagebreakBottom())
224                 os << "\\pagebreak_bottom ";
225         
226         // Start of appendix?
227         if (params().startOfAppendix())
228                 os << "\\start_of_appendix ";
229         
230         // Noindent?
231         if (params().noindent())
232                 os << "\\noindent ";
233         
234         // Alignment?
235         if (params().align() != LYX_ALIGN_LAYOUT) {
236                 int h = 0;
237                 switch (params().align()) {
238                 case LYX_ALIGN_LEFT: h = 1; break;
239                 case LYX_ALIGN_RIGHT: h = 2; break;
240                 case LYX_ALIGN_CENTER: h = 3; break;
241                 default: h = 0; break;
242                 }
243                 os << "\\align " << string_align[h] << " ";
244         }
245         
246         // bibitem  ale970302
247         if (bibkey)
248                 bibkey->write(buf, os);
249         
250         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
251         
252         int column = 0;
253         for (pos_type i = 0; i < size(); ++i) {
254                 if (!i) {
255                         os << "\n";
256                         column = 0;
257                 }
258                 
259                 // Write font changes
260                 LyXFont font2 = getFontSettings(bparams, i);
261                 if (font2 != font1) {
262 #ifndef INHERIT_LANGUAGE
263                         font2.lyxWriteChanges(font1, os);
264 #else
265                         font2.lyxWriteChanges(font1, bparams.language, os);
266 #endif
267                         column = 0;
268                         font1 = font2;
269                 }
270                 
271                 value_type const c = getChar(i);
272                 switch (c) {
273                 case META_INSET:
274                 {
275                         Inset const * inset = getInset(i);
276                         if (inset)
277                                 if (inset->directWrite()) {
278                                         // international char, let it write
279                                         // code directly so it's shorter in
280                                         // the file
281                                         inset->write(buf, os);
282                                 } else {
283                                         os << "\n\\begin_inset ";
284                                         inset->write(buf, os);
285                                         os << "\n\\end_inset \n\n";
286                                         column = 0;
287                                 }
288                 }
289                 break;
290                 case META_NEWLINE: 
291                         os << "\n\\newline \n";
292                         column = 0;
293                         break;
294                 case META_HFILL: 
295                         os << "\n\\hfill \n";
296                         column = 0;
297                         break;
298                 case '\\':
299                         os << "\n\\backslash \n";
300                         column = 0;
301                         break;
302                 case '.':
303                         if (i + 1 < size() && getChar(i + 1) == ' ') {
304                                 os << ".\n";
305                                 column = 0;
306                         } else
307                                 os << ".";
308                         break;
309                 default:
310                         if ((column > 70 && c == ' ')
311                             || column > 79) {
312                                 os << "\n";
313                                 column = 0;
314                         }
315                         // this check is to amend a bug. LyX sometimes
316                         // inserts '\0' this could cause problems.
317                         if (c != '\0')
318                                 os << c;
319                         else
320                                 lyxerr << "ERROR (Paragraph::writeFile):"
321                                         " NULL char in structure." << endl;
322                         ++column;
323                         break;
324                 }
325         }
326         
327         // now write the next paragraph
328         if (next_)
329                 next_->writeFile(buf, os, bparams, dth);
330 }
331
332
333 void Paragraph::validate(LaTeXFeatures & features) const
334 {
335         BufferParams const & bparams = features.bufferParams();
336
337         // check the params.
338         if (params().lineTop() || params().lineBottom())
339                 features.require("lyxline");
340         if (!params().spacing().isDefault())
341                 features.require("setspace");
342         
343         // then the layouts
344         features.useLayout(getLayout());
345
346         // then the fonts
347         Language const * doc_language = bparams.language;
348         
349         for (Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
350              cit != pimpl_->fontlist.end(); ++cit) {
351                 if (cit->font().noun() == LyXFont::ON) {
352                         lyxerr[Debug::LATEX] << "font.noun: "
353                                              << cit->font().noun()
354                                              << endl;
355                         features.require("noun");
356                         lyxerr[Debug::LATEX] << "Noun enabled. Font: "
357                                              << cit->font().stateText(0)
358                                              << endl;
359                 }
360                 switch (cit->font().color()) {
361                 case LColor::none:
362                 case LColor::inherit:
363                 case LColor::ignore:
364                         // probably we should put here all interface colors used for
365                         // font displaying! For now I just add this ones I know of (Jug)
366                 case LColor::latex:
367                 case LColor::note:
368                         break;
369                 default:
370                         features.require("color");
371                         lyxerr[Debug::LATEX] << "Color enabled. Font: "
372                                              << cit->font().stateText(0)
373                                              << endl;
374                 }
375
376                 Language const * language = cit->font().language();
377                 if (language->babel() != doc_language->babel() &&
378                     language != ignore_language &&
379 #ifdef INHERIT_LANGUAGE
380                     language != inherit_language &&
381 #endif
382                     language != latex_language)
383                 {
384                         features.useLanguage(language);
385                         lyxerr[Debug::LATEX] << "Found language "
386                                              << language->babel() << endl;
387                 }
388         }
389
390         // then the insets
391         LyXLayout const & layout =
392              textclasslist.Style(bparams.textclass, getLayout());
393
394         for (InsetList::const_iterator cit = insetlist.begin();
395              cit != insetlist.end(); ++cit) {
396                 if (cit->inset) {
397                         cit->inset->validate(features);
398                         if (layout.needprotect &&
399                             cit->inset->lyxCode() == Inset::FOOT_CODE)
400                                 features.require("NeedLyXFootnoteCode");
401                 }
402         }
403 }
404
405
406 // First few functions needed for cut and paste and paragraph breaking.
407 void Paragraph::copyIntoMinibuffer(Buffer const & buffer, pos_type pos) const
408 {
409         BufferParams bparams = buffer.params;
410
411         minibuffer_char = getChar(pos);
412         minibuffer_font = getFontSettings(bparams, pos);
413         minibuffer_inset = 0;
414         if (minibuffer_char == Paragraph::META_INSET) {
415                 if (getInset(pos)) {
416                         minibuffer_inset = getInset(pos)->clone(buffer);
417                 } else {
418                         minibuffer_inset = 0;
419                         minibuffer_char = ' ';
420                         // This reflects what GetInset() does (ARRae)
421                 }
422         }
423 }
424
425
426 void Paragraph::cutIntoMinibuffer(BufferParams const & bparams, pos_type pos)
427 {
428         minibuffer_char = getChar(pos);
429         minibuffer_font = getFontSettings(bparams, pos);
430         minibuffer_inset = 0;
431         if (minibuffer_char == Paragraph::META_INSET) {
432                 if (getInset(pos)) {
433                         minibuffer_inset = getInset(pos);
434                         // This is a little hack since I want exactly
435                         // the inset, not just a clone. Otherwise
436                         // the inset would be deleted when calling Erase(pos)
437                         // find the entry
438                         InsetTable search_elem(pos, 0);
439                         InsetList::iterator it =
440                                 lower_bound(insetlist.begin(),
441                                             insetlist.end(),
442                                             search_elem, Pimpl::matchIT());
443                         if (it != insetlist.end() && it->pos == pos)
444                                 it->inset = 0;
445                         // the inset is not in a paragraph anymore
446                         minibuffer_inset->parOwner(0);
447                 } else {
448                         minibuffer_inset = 0;
449                         minibuffer_char = ' ';
450                         // This reflects what GetInset() does (ARRae)
451                 }
452
453         }
454
455         // Erase(pos); now the caller is responsible for that.
456 }
457
458
459 bool Paragraph::insertFromMinibuffer(pos_type pos)
460 {
461         if (minibuffer_char == Paragraph::META_INSET) {
462                 if (!insetAllowed(minibuffer_inset->lyxCode())) {
463                         return false;
464                 }
465                 insertInset(pos, minibuffer_inset, minibuffer_font);
466         } else {
467                 LyXFont f = minibuffer_font;
468                 if (!checkInsertChar(f)) {
469                         return false;
470                 }
471                 insertChar(pos, minibuffer_char, f);
472         }
473         return true;
474 }
475
476 // end of minibuffer
477
478
479
480 void Paragraph::clear()
481 {
482         params().clear();
483         
484         layout = 0;
485         bibkey = 0;
486 }
487
488
489 void Paragraph::erase(pos_type pos)
490 {
491         pimpl_->erase(pos);
492 }
493
494
495 bool Paragraph::checkInsertChar(LyXFont & font)
496 {
497         if (pimpl_->inset_owner)
498                 return pimpl_->inset_owner->checkInsertChar(font);
499         return true;
500 }
501
502
503 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
504 {
505         LyXFont const f(LyXFont::ALL_INHERIT);
506         insertChar(pos, c, f);
507 }
508
509
510 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
511                            LyXFont const & font)
512 {
513         pimpl_->insertChar(pos, c, font);
514 }
515
516
517 void Paragraph::insertInset(pos_type pos, Inset * inset)
518 {
519         LyXFont const f(LyXFont::ALL_INHERIT);
520         insertInset(pos, inset, f);
521 }
522
523
524 void Paragraph::insertInset(pos_type pos, Inset * inset, LyXFont const & font)
525 {
526         pimpl_->insertInset(pos, inset, font);
527 }
528
529
530 bool Paragraph::insetAllowed(Inset::Code code)
531 {
532         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
533         
534         if (pimpl_->inset_owner)
535                 return pimpl_->inset_owner->insetAllowed(code);
536         return true;
537 }
538
539
540 Inset * Paragraph::getInset(pos_type pos)
541 {
542         lyx::Assert(pos < size());
543
544         // Find the inset.
545         InsetTable search_inset(pos, 0);
546         InsetList::iterator it = lower_bound(insetlist.begin(),
547                                              insetlist.end(),
548                                              search_inset, Pimpl::matchIT());
549         if (it != insetlist.end() && it->pos == pos)
550                 return it->inset;
551
552         lyxerr << "ERROR (Paragraph::getInset): "
553                 "Inset does not exist: " << pos << endl;
554         //::raise(SIGSTOP);
555         
556         // text[pos] = ' '; // WHY!!! does this set the pos to ' '????
557         // Did this commenting out introduce a bug? So far I have not
558         // see any, please enlighten me. (Lgb)
559         // My guess is that since the inset does not exist, we might
560         // as well replace it with a space to prevent craches. (Asger)
561         return 0;
562 }
563
564
565 Inset const * Paragraph::getInset(pos_type pos) const
566 {
567         lyx::Assert(pos < size());
568
569         // Find the inset.
570         InsetTable search_inset(pos, 0);
571         InsetList::const_iterator cit = lower_bound(insetlist.begin(),
572                                                     insetlist.end(),
573                                                     search_inset, Pimpl::matchIT());
574         if (cit != insetlist.end() && cit->pos == pos)
575                 return cit->inset;
576
577         lyxerr << "ERROR (Paragraph::GetInset): "
578                 "Inset does not exist: " << pos << endl;
579         //::raise(SIGSTOP);
580         //text[pos] = ' '; // WHY!!! does this set the pos to ' '????
581         // Did this commenting out introduce a bug? So far I have not
582         // see any, please enlighten me. (Lgb)
583         // My guess is that since the inset does not exist, we might
584         // as well replace it with a space to prevent craches. (Asger)
585         return 0;
586 }
587
588
589 // Gets uninstantiated font setting at position.
590 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
591                                          pos_type pos) const
592 {
593         lyx::Assert(pos <= size());
594         
595         Pimpl::FontTable search_font(pos, LyXFont());
596         Pimpl::FontList::const_iterator cit = lower_bound(pimpl_->fontlist.begin(),
597                                                    pimpl_->fontlist.end(),
598                                                    search_font, Pimpl::matchFT());
599         LyXFont retfont;
600         if (cit != pimpl_->fontlist.end()) {
601                 retfont = cit->font();
602         } else if (pos == size() && size()) {
603                 retfont = getFontSettings(bparams, pos - 1);
604         } else
605                 retfont = LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
606 #ifdef INHERIT_LANGUAGE
607         if (retfont.language() == inherit_language)
608                 retfont.setLanguage(bparams.language);
609 #endif
610
611         return retfont;
612 }
613
614
615 // Gets uninstantiated font setting at position 0
616 LyXFont const Paragraph::getFirstFontSettings() const
617 {
618         if (size() > 0 && !pimpl_->fontlist.empty())
619                 return pimpl_->fontlist[0].font();
620         
621         return LyXFont(LyXFont::ALL_INHERIT);
622 }
623
624
625 // Gets the fully instantiated font at a given position in a paragraph
626 // This is basically the same function as LyXText::GetFont() in text2.C.
627 // The difference is that this one is used for generating the LaTeX file,
628 // and thus cosmetic "improvements" are disallowed: This has to deliver
629 // the true picture of the buffer. (Asger)
630 // If position is -1, we get the layout font of the paragraph.
631 // If position is -2, we get the font of the manual label of the paragraph.
632 LyXFont const Paragraph::getFont(BufferParams const & bparams,
633                                  pos_type pos) const
634 {
635         lyx::Assert(pos >= 0);
636         
637         LyXLayout const & layout =
638                 textclasslist.Style(bparams.textclass, 
639                                     getLayout());
640         pos_type main_body = 0;
641         if (layout.labeltype == LABEL_MANUAL)
642                 main_body = beginningOfMainBody();
643
644         LyXFont layoutfont;
645         if (pos < main_body)
646                 layoutfont = layout.labelfont;
647         else
648                 layoutfont = layout.font;
649         
650         LyXFont tmpfont = getFontSettings(bparams, pos);
651 #ifndef INHERIT_LANGUAGE
652         tmpfont.realize(layoutfont);
653 #else
654         tmpfont.realize(layoutfont, bparams.language);
655 #endif
656
657         return pimpl_->realizeFont(tmpfont, bparams);
658 }
659
660
661 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams) const
662 {
663         LyXLayout const & layout =
664                 textclasslist.Style(bparams.textclass, getLayout());
665         
666         LyXFont tmpfont = layout.labelfont;
667         tmpfont.setLanguage(getParLanguage(bparams));
668
669         return pimpl_->realizeFont(tmpfont, bparams);
670 }
671
672
673 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams) const
674 {
675         LyXLayout const & layout =
676                 textclasslist.Style(bparams.textclass, 
677                                     getLayout());
678
679         LyXFont tmpfont = layout.font;
680         tmpfont.setLanguage(getParLanguage(bparams));
681
682         return pimpl_->realizeFont(tmpfont, bparams);
683 }
684
685
686 /// Returns the height of the highest font in range
687 LyXFont::FONT_SIZE
688 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
689                               LyXFont::FONT_SIZE const def_size) const
690 {
691         if (pimpl_->fontlist.empty())
692                 return def_size;
693
694         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
695         Pimpl::FontTable end_search(endpos, LyXFont());
696         Pimpl::FontList::const_iterator end_it =
697                 lower_bound(pimpl_->fontlist.begin(),
698                             pimpl_->fontlist.end(),
699                             end_search, Pimpl::matchFT());
700         if (end_it != pimpl_->fontlist.end())
701                 ++end_it;
702
703         Pimpl::FontTable start_search(startpos, LyXFont());
704         Pimpl::FontList::const_iterator cit =
705                 lower_bound(pimpl_->fontlist.begin(),
706                             pimpl_->fontlist.end(),
707                             start_search, Pimpl::matchFT());
708         
709         for (; cit != end_it; ++cit) {
710                 LyXFont::FONT_SIZE size = cit->font().size();
711                 if (size == LyXFont::INHERIT_SIZE)
712                         size = def_size;
713                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
714                         maxsize = size;
715         }
716         return maxsize;
717 }
718
719
720 Paragraph::value_type
721 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
722 {
723         value_type c = getChar(pos);
724         if (!lyxrc.rtl_support)
725                 return c;
726
727         value_type uc = c;
728         switch (c) {
729         case '(':
730                 uc = ')';
731                 break;
732         case ')':
733                 uc = '(';
734                 break;
735         case '[':
736                 uc = ']';
737                 break;
738         case ']':
739                 uc = '[';
740                 break;
741         case '{':
742                 uc = '}';
743                 break;
744         case '}':
745                 uc = '{';
746                 break;
747         case '<':
748                 uc = '>';
749                 break;
750         case '>':
751                 uc = '<';
752                 break;
753         }
754         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
755                 return uc;
756         else
757                 return c;
758 }
759
760
761 void Paragraph::setFont(pos_type pos, LyXFont const & font)
762 {
763         lyx::Assert(pos <= size());
764
765         // First, reduce font against layout/label font
766         // Update: The SetCharFont() routine in text2.C already
767         // reduces font, so we don't need to do that here. (Asger)
768         // No need to simplify this because it will disappear
769         // in a new kernel. (Asger)
770         // Next search font table
771
772         Pimpl::FontTable search_font(pos, LyXFont());
773         Pimpl::FontList::iterator it = lower_bound(pimpl_->fontlist.begin(),
774                                             pimpl_->fontlist.end(),
775                                             search_font, Pimpl::matchFT());
776         unsigned int i = it - pimpl_->fontlist.begin();
777         bool notfound = it == pimpl_->fontlist.end();
778
779         if (!notfound && pimpl_->fontlist[i].font() == font)
780                 return;
781
782         bool begin = pos == 0 || notfound ||
783                 (i > 0 && pimpl_->fontlist[i-1].pos() == pos - 1);
784         // Is position pos is a beginning of a font block?
785         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
786         // Is position pos is the end of a font block?
787         if (begin && end) { // A single char block
788                 if (i + 1 < pimpl_->fontlist.size() &&
789                     pimpl_->fontlist[i + 1].font() == font) {
790                         // Merge the singleton block with the next block
791                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
792                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
793                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i-1);
794                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
795                         // Merge the singleton block with the previous block
796                         pimpl_->fontlist[i - 1].pos(pos);
797                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
798                 } else
799                         pimpl_->fontlist[i].font(font);
800         } else if (begin) {
801                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
802                         pimpl_->fontlist[i - 1].pos(pos);
803                 else
804                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
805                                         Pimpl::FontTable(pos, font));
806         } else if (end) {
807                 pimpl_->fontlist[i].pos(pos - 1);
808                 if (!(i + 1 < pimpl_->fontlist.size() &&
809                       pimpl_->fontlist[i + 1].font() == font))
810                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
811                                         Pimpl::FontTable(pos, font));
812         } else { // The general case. The block is splitted into 3 blocks
813                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i, 
814                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
815                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
816                                 Pimpl::FontTable(pos, font));
817         }
818 }
819
820
821
822 void Paragraph::next(Paragraph * p)
823 {
824         next_ = p;
825 }
826
827
828 // This function is able to hide closed footnotes.
829 Paragraph * Paragraph::next()
830 {
831         return next_;
832 }
833
834
835 Paragraph const * Paragraph::next() const
836 {
837         return next_;
838 }
839
840
841 void Paragraph::previous(Paragraph * p)
842 {
843         previous_ = p;
844 }
845
846
847 // This function is able to hide closed footnotes.
848 Paragraph * Paragraph::previous()
849 {
850         return previous_;
851 }
852
853
854 // This function is able to hide closed footnotes.
855 Paragraph const * Paragraph::previous() const
856 {
857         return previous_;
858 }
859
860
861 void Paragraph::breakParagraph(BufferParams const & bparams,
862                                   pos_type pos,
863                                   int flag)
864 {
865         // create a new paragraph
866         Paragraph * tmp = new Paragraph(this);
867         // remember to set the inset_owner
868         tmp->setInsetOwner(inInset());
869         
870         // this is an idea for a more userfriendly layout handling, I will
871         // see what the users say
872         
873         // layout stays the same with latex-environments
874         if (flag) {
875                 tmp->setOnlyLayout(layout);
876                 tmp->setLabelWidthString(params().labelWidthString());
877         }
878         
879         if (size() > pos || !size() || flag == 2) {
880                 tmp->setOnlyLayout(layout);
881                 tmp->params().align(params().align());
882                 tmp->setLabelWidthString(params().labelWidthString());
883                 
884                 tmp->params().lineBottom(params().lineBottom());
885                 params().lineBottom(false);
886                 tmp->params().pagebreakBottom(params().pagebreakBottom());
887                 params().pagebreakBottom(false);
888                 tmp->params().spaceBottom(params().spaceBottom());
889                 params().spaceBottom(VSpace(VSpace::NONE));
890                 
891                 tmp->params().depth(params().depth());
892                 tmp->params().noindent(params().noindent());
893                 
894                 // copy everything behind the break-position
895                 // to the new paragraph
896                 pos_type pos_end = size() - 1;
897                 pos_type i = pos;
898                 pos_type j = pos;
899                 for (; i <= pos_end; ++i) {
900                         cutIntoMinibuffer(bparams, i);
901                         if (tmp->insertFromMinibuffer(j - pos))
902                                 ++j;
903                 }
904                 for (i = pos_end; i >= pos; --i) {
905                         erase(i);
906                 }
907         }
908         
909         // just an idea of me
910         if (!pos) {
911                 tmp->params().lineTop(params().lineTop());
912                 tmp->params().pagebreakTop(params().pagebreakTop());
913                 tmp->params().spaceTop(params().spaceTop());
914                 tmp->bibkey = bibkey;
915                 clear();
916                 // layout stays the same with latex-environments
917                 if (flag) {
918                         setOnlyLayout(tmp->layout);
919                         setLabelWidthString(tmp->params().labelWidthString());
920                         params().depth(tmp->params().depth());
921                 }
922         }
923 }
924         
925
926 void Paragraph::makeSameLayout(Paragraph const * par)
927 {
928         layout = par->layout;
929         // move to pimpl?
930         params() = par->params();
931 }
932
933
934 int Paragraph::stripLeadingSpaces(lyx::textclass_type tclass) 
935 {
936         if (textclasslist.Style(tclass, getLayout()).free_spacing ||
937                 isFreeSpacing())
938         {
939                 return 0;
940         }
941         
942         int i = 0;
943         while (size() && (isNewline(0) || isLineSeparator(0))) {
944                 erase(0);
945                 ++i;
946         }
947
948         return i;
949 }
950
951
952 bool Paragraph::hasSameLayout(Paragraph const * par) const
953 {
954         return 
955                 par->layout == layout &&
956                 params().sameLayout(par->params());
957 }
958
959
960 void Paragraph::breakParagraphConservative(BufferParams const & bparams,
961                                            pos_type pos)
962 {
963         // create a new paragraph
964         Paragraph * tmp = new Paragraph(this);
965         tmp->makeSameLayout(this);
966
967         // When can pos > Last()?
968         // I guess pos == Last() is possible.
969         if (size() > pos) {
970                 // copy everything behind the break-position to the new
971                 // paragraph
972                 pos_type pos_end = size() - 1;
973
974                 //pos_type i = pos;
975                 //pos_type j = pos;
976                 for (pos_type i = pos, j = pos; i <= pos_end; ++i) {
977                         cutIntoMinibuffer(bparams, i);
978                         if (tmp->insertFromMinibuffer(j - pos))
979                                 ++j;
980                 }
981                 
982                 for (pos_type k = pos_end; k >= pos; --k) {
983                         erase(k);
984                 }
985         }
986 }
987
988
989 // Be carefull, this does not make any check at all.
990 // This method has wrong name, it combined this par with the next par.
991 // In that sense it is the reverse of break paragraph. (Lgb)
992 void Paragraph::pasteParagraph(BufferParams const & bparams)
993 {
994         // copy the next paragraph to this one
995         Paragraph * the_next = next();
996
997         // first the DTP-stuff
998         params().lineBottom(the_next->params().lineBottom());
999         params().spaceBottom(the_next->params().spaceBottom());
1000         params().pagebreakBottom(the_next->params().pagebreakBottom());
1001
1002         pos_type pos_end = the_next->pimpl_->size() - 1;
1003         pos_type pos_insert = size();
1004
1005         // ok, now copy the paragraph
1006         for (pos_type i = 0, j = 0; i <= pos_end; ++i) {
1007                 the_next->cutIntoMinibuffer(bparams, i);
1008                 if (insertFromMinibuffer(pos_insert + j))
1009                         ++j;
1010         }
1011
1012         // delete the next paragraph
1013         Paragraph * ppar = the_next->previous_;
1014         Paragraph * npar = the_next->next_;
1015         delete the_next;
1016         ppar->next(npar);
1017 }
1018
1019
1020 int Paragraph::getEndLabel(BufferParams const & bparams) const
1021 {
1022         Paragraph const * par = this;
1023         depth_type par_depth = getDepth();
1024         while (par) {
1025                 layout_type layout = par->getLayout();
1026                 int const endlabeltype =
1027                         textclasslist.Style(bparams.textclass,
1028                                             layout).endlabeltype;
1029                 if (endlabeltype != END_LABEL_NO_LABEL) {
1030                         if (!next_)
1031                                 return endlabeltype;
1032
1033                         depth_type const next_depth = next_->getDepth();
1034                         if (par_depth > next_depth ||
1035                             (par_depth == next_depth
1036                              && layout != next_->getLayout()))
1037                                 return endlabeltype;
1038                         break;
1039                 }
1040                 if (par_depth == 0)
1041                         break;
1042                 par = par->outerHook();
1043                 if (par)
1044                         par_depth = par->getDepth();
1045         }
1046         return END_LABEL_NO_LABEL;
1047 }
1048
1049
1050 Paragraph::depth_type Paragraph::getDepth() const
1051 {
1052         return params().depth();
1053 }
1054
1055
1056 Paragraph::depth_type Paragraph::getMaxDepthAfter(Buffer const * buffer) const
1057 {
1058         const bool isenv = textclasslist.Style(buffer->params.textclass,
1059                                                getLayout()).isEnvironment();
1060
1061         if (isenv)
1062                 return params().depth() + 1;
1063         else
1064                 return params().depth();
1065
1066 }
1067
1068 char Paragraph::getAlign() const
1069 {
1070         return params().align();
1071 }
1072
1073
1074 string const & Paragraph::getLabelstring() const
1075 {
1076         return params().labelString();
1077 }
1078
1079
1080 int Paragraph::getFirstCounter(int i) const
1081 {
1082         return pimpl_->counter_[i];
1083 }
1084
1085
1086 // the next two functions are for the manual labels
1087 string const Paragraph::getLabelWidthString() const
1088 {
1089         if (!params().labelWidthString().empty())
1090                 return params().labelWidthString();
1091         else
1092                 return _("Senseless with this layout!");
1093 }
1094
1095
1096 void Paragraph::setLabelWidthString(string const & s)
1097 {
1098         params().labelWidthString(s);
1099 }
1100
1101
1102 void Paragraph::setOnlyLayout(layout_type new_layout)
1103 {
1104         layout = new_layout;
1105 }
1106
1107
1108 void Paragraph::setLayout(layout_type new_layout)
1109 {
1110         layout = new_layout;
1111         params().labelWidthString(string());
1112         params().align(LYX_ALIGN_LAYOUT);
1113         params().spaceTop(VSpace(VSpace::NONE));
1114         params().spaceBottom(VSpace(VSpace::NONE));
1115         params().spacing(Spacing(Spacing::Default));
1116 }
1117
1118
1119 // if the layout of a paragraph contains a manual label, the beginning of the 
1120 // main body is the beginning of the second word. This is what the par-
1121 // function returns. If the layout does not contain a label, the main
1122 // body always starts with position 0. This differentiation is necessary,
1123 // because there cannot be a newline or a blank <= the beginning of the 
1124 // main body in TeX.
1125
1126 int Paragraph::beginningOfMainBody() const
1127 {
1128         // Unroll the first two cycles of the loop
1129         // and remember the previous character to
1130         // remove unnecessary GetChar() calls
1131         pos_type i = 0;
1132         if (i < size()
1133             && getChar(i) != Paragraph::META_NEWLINE) {
1134                 ++i;
1135                 char previous_char = 0;
1136                 char temp = 0; 
1137                 if (i < size()
1138                     && (previous_char = getChar(i)) != Paragraph::META_NEWLINE) {
1139                         // Yes, this  ^ is supposed to be "= " not "=="
1140                         ++i;
1141                         while (i < size()
1142                                && previous_char != ' '
1143                                && (temp = getChar(i)) != Paragraph::META_NEWLINE) {
1144                                 ++i;
1145                                 previous_char = temp;
1146                         }
1147                 }
1148         }
1149
1150         return i;
1151 }
1152
1153
1154 Paragraph * Paragraph::depthHook(depth_type depth)
1155 {
1156         Paragraph * newpar = this;
1157
1158         do {
1159                 newpar = newpar->previous();
1160         } while (newpar && newpar->getDepth() > depth);
1161
1162         if (!newpar) {
1163                 if (previous() || getDepth())
1164                         lyxerr << "ERROR (Paragraph::DepthHook): "
1165                                 "no hook." << endl;
1166                 newpar = this;
1167         }
1168
1169         return newpar;
1170 }
1171
1172
1173 Paragraph const * Paragraph::depthHook(depth_type depth) const
1174 {
1175         Paragraph const * newpar = this;
1176
1177         do {
1178                 newpar = newpar->previous();
1179         } while (newpar && newpar->getDepth() > depth);
1180
1181         if (!newpar) {
1182                 if (previous() || getDepth())
1183                         lyxerr << "ERROR (Paragraph::DepthHook): "
1184                                 "no hook." << endl;
1185                 newpar = this;
1186         }
1187
1188         return newpar;
1189 }
1190
1191 Paragraph * Paragraph::outerHook()
1192 {
1193         if (!getDepth())
1194                 return 0;
1195         return depthHook(depth_type(getDepth() - 1));
1196 }
1197
1198 Paragraph const * Paragraph::outerHook() const
1199 {
1200         if (!getDepth())
1201                 return 0;
1202         return depthHook(depth_type(getDepth() - 1));
1203 }
1204
1205
1206 Paragraph::inset_iterator
1207 Paragraph::InsetIterator(pos_type pos)
1208 {
1209         InsetTable search_inset(pos, 0);
1210         InsetList::iterator it = lower_bound(insetlist.begin(),
1211                                              insetlist.end(),
1212                                              search_inset, Pimpl::matchIT());
1213         return inset_iterator(it);
1214 }
1215
1216
1217 // returns -1 if inset not found
1218 int Paragraph::getPositionOfInset(Inset const * inset) const
1219 {
1220         // Find the entry.
1221         for (InsetList::const_iterator cit = insetlist.begin();
1222              cit != insetlist.end(); ++cit) {
1223                 if (cit->inset == inset) {
1224                         return cit->pos;
1225                 }
1226         }
1227         if (inset == bibkey)
1228                 return 0;
1229
1230         return -1;
1231 }
1232
1233
1234 Paragraph * Paragraph::TeXOnePar(Buffer const * buf,
1235                                  BufferParams const & bparams,
1236                                  ostream & os, TexRow & texrow,
1237                                  bool moving_arg)
1238 {
1239         lyxerr[Debug::LATEX] << "TeXOnePar...     " << this << endl;
1240         Inset const * in = inInset();
1241         bool further_blank_line = false;
1242         LyXLayout style;
1243         
1244         // well we have to check if we are in an inset with unlimited
1245         // lenght (all in one row) if that is true then we don't allow
1246         // any special options in the paragraph and also we don't allow
1247         // any environment other then "Standard" to be valid!
1248         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
1249                 style = textclasslist.Style(bparams.textclass, layout);
1250
1251                 if (params().startOfAppendix()) {
1252                         os << "\\appendix\n";
1253                         texrow.newline();
1254                 }
1255
1256                 if (!params().spacing().isDefault()
1257                         && (!previous() || !previous()->hasSameLayout(this))) {
1258                         os << params().spacing().writeEnvirBegin() << "\n";
1259                         texrow.newline();
1260                 }
1261         
1262                 if (tex_code_break_column && style.isCommand()) {
1263                         os << '\n';
1264                         texrow.newline();
1265                 }
1266
1267                 if (params().pagebreakTop()) {
1268                         os << "\\newpage";
1269                         further_blank_line = true;
1270                 }
1271                 if (params().spaceTop().kind() != VSpace::NONE) {
1272                         os << params().spaceTop().asLatexCommand(bparams);
1273                         further_blank_line = true;
1274                 }
1275
1276                 if (params().lineTop()) {
1277                         os << "\\lyxline{\\" << getFont(bparams, 0).latexSize() << '}'
1278                            << "\\vspace{-1\\parskip}";
1279                         further_blank_line = true;
1280                 }
1281
1282                 if (further_blank_line) {
1283                         os << '\n';
1284                         texrow.newline();
1285                 }
1286         } else {
1287                 style = textclasslist.Style(bparams.textclass, 0);
1288         }
1289
1290         Language const * language = getParLanguage(bparams);
1291         Language const * doc_language = bparams.language;
1292         Language const * previous_language = previous()
1293                 ? previous()->getParLanguage(bparams) : doc_language;
1294
1295         if (language->babel() != previous_language->babel()
1296             // check if we already put language command in TeXEnvironment()
1297             && !(style.isEnvironment()
1298                  && (!previous() || previous()->layout != layout ||
1299                          previous()->params().depth() != params().depth())))
1300         {
1301                 if (!lyxrc.language_command_end.empty() &&
1302                     previous_language->babel() != doc_language->babel())
1303                 {
1304                         os << subst(lyxrc.language_command_end, "$$lang",
1305                                     previous_language->babel())
1306                            << endl;
1307                         texrow.newline();
1308                 }
1309
1310                 if (lyxrc.language_command_end.empty() ||
1311                     language->babel() != doc_language->babel())
1312                 {
1313                         os << subst(lyxrc.language_command_begin, "$$lang",
1314                                     language->babel())
1315                            << endl;
1316                         texrow.newline();
1317                 }
1318         }
1319
1320         if (bparams.inputenc == "auto" &&
1321             language->encoding() != previous_language->encoding()) {
1322                 os << "\\inputencoding{"
1323                    << language->encoding()->LatexName()
1324                    << "}" << endl;
1325                 texrow.newline();
1326         }
1327
1328         switch (style.latextype) {
1329         case LATEX_COMMAND:
1330                 os << '\\'
1331                    << style.latexname()
1332                    << style.latexparam();
1333                 break;
1334         case LATEX_ITEM_ENVIRONMENT:
1335                 if (bibkey) {
1336                         bibkey->latex(buf, os, false, false);
1337                 } else
1338                         os << "\\item ";
1339                 break;
1340         case LATEX_LIST_ENVIRONMENT:
1341                 os << "\\item ";
1342                 break;
1343         default:
1344                 break;
1345         }
1346
1347         bool need_par = simpleTeXOnePar(buf, bparams, os, texrow, moving_arg);
1348  
1349         // Make sure that \\par is done with the font of the last
1350         // character if this has another size as the default.
1351         // This is necessary because LaTeX (and LyX on the screen)
1352         // calculates the space between the baselines according
1353         // to this font. (Matthias)
1354         //
1355         // Is this really needed ? (Dekel)
1356         // We do not need to use to change the font for the last paragraph
1357         // or for a command.
1358         LyXFont const font =
1359                 (size() == 0
1360                  ? getLayoutFont(bparams) : getFont(bparams, size() - 1));
1361
1362         bool is_command = style.isCommand();
1363         
1364         if (style.resfont.size() != font.size() && next_ && !is_command) {
1365                 if (!need_par)
1366                         os << "{";
1367                 os << "\\" << font.latexSize() << " \\par}";
1368         } else if (need_par) {
1369                 os << "\\par}";
1370         } else if (is_command)
1371                 os << "}";
1372
1373         switch (style.latextype) {
1374         case LATEX_ITEM_ENVIRONMENT:
1375         case LATEX_LIST_ENVIRONMENT:
1376                 if (next_ && (params().depth() < next_->params().depth())) {
1377                         os << '\n';
1378                         texrow.newline();
1379                 }
1380                 break;
1381         case LATEX_ENVIRONMENT:
1382                 // if its the last paragraph of the current environment
1383                 // skip it otherwise fall through
1384                 if (next_
1385                     && (next_->layout != layout
1386                         || next_->params().depth() != params().depth()))
1387                         break;
1388                 // fall through possible
1389         default:
1390                 // we don't need it for the last paragraph!!!
1391                 if (next_) {
1392                         os << '\n';
1393                         texrow.newline();
1394                 }
1395         }
1396         
1397         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
1398                 further_blank_line = false;
1399                 if (params().lineBottom()) {
1400                         os << "\\lyxline{\\" << font.latexSize() << '}';
1401                         further_blank_line = true;
1402                 }
1403
1404                 if (params().spaceBottom().kind() != VSpace::NONE) {
1405                         os << params().spaceBottom().asLatexCommand(bparams);
1406                         further_blank_line = true;
1407                 }
1408
1409                 if (params().pagebreakBottom()) {
1410                         os << "\\newpage";
1411                         further_blank_line = true;
1412                 }
1413
1414                 if (further_blank_line) {
1415                         os << '\n';
1416                         texrow.newline();
1417                 }
1418
1419                 if (!params().spacing().isDefault()
1420                         && (!next_ || !next_->hasSameLayout(this))) {
1421                         os << params().spacing().writeEnvirEnd() << "\n";
1422                         texrow.newline();
1423                 }
1424         }
1425         
1426         // we don't need it for the last paragraph!!!
1427         if (next_) {
1428                 os << '\n';
1429                 texrow.newline();
1430         } else {
1431                 // Since \selectlanguage write the language to the aux file,
1432                 // we need to reset the language at the end of footnote or
1433                 // float.
1434
1435                 if (language->babel() != doc_language->babel()) {
1436                         if (lyxrc.language_command_end.empty())
1437                                 os << subst(lyxrc.language_command_begin,
1438                                             "$$lang",
1439                                             doc_language->babel())
1440                                    << endl;
1441                         else
1442                                 os << subst(lyxrc.language_command_end,
1443                                             "$$lang",
1444                                             language->babel())
1445                                    << endl;
1446                         texrow.newline();
1447                 }
1448         }
1449
1450         lyxerr[Debug::LATEX] << "TeXOnePar...done " << next_ << endl;
1451         return next_;
1452 }
1453
1454 // This could go to ParagraphParameters if we want to
1455 int Paragraph::startTeXParParams(BufferParams const & bparams,
1456                                  ostream & os) const
1457 {
1458         int column = 0;
1459         
1460         if (params().noindent()) {
1461                 os << "\\noindent ";
1462                 column += 10;
1463         }
1464                         
1465         switch (params().align()) {
1466         case LYX_ALIGN_NONE:
1467         case LYX_ALIGN_BLOCK:
1468         case LYX_ALIGN_LAYOUT:
1469         case LYX_ALIGN_SPECIAL:
1470                 break;
1471         case LYX_ALIGN_LEFT:
1472                 if (getParLanguage(bparams)->babel() != "hebrew") {
1473                         os << "\\begin{flushleft}";
1474                         column += 17;
1475                 } else {
1476                         os << "\\begin{flushright}";
1477                         column += 18;
1478                 }
1479                 break;
1480         case LYX_ALIGN_RIGHT:
1481                 if (getParLanguage(bparams)->babel() != "hebrew") {
1482                         os << "\\begin{flushright}";
1483                         column += 18;
1484                 } else {
1485                         os << "\\begin{flushleft}";
1486                         column += 17;
1487                 }
1488                 break;
1489         case LYX_ALIGN_CENTER:
1490                 os << "\\begin{center}";
1491                 column += 14;
1492                 break;
1493         }
1494         
1495         return column;
1496 }
1497
1498 // This could go to ParagraphParameters if we want to
1499 int Paragraph::endTeXParParams(BufferParams const & bparams,
1500                                ostream & os) const
1501 {
1502         int column = 0;
1503         
1504         switch (params().align()) {
1505         case LYX_ALIGN_NONE:
1506         case LYX_ALIGN_BLOCK:
1507         case LYX_ALIGN_LAYOUT:
1508         case LYX_ALIGN_SPECIAL:
1509                 break;
1510         case LYX_ALIGN_LEFT:
1511                 if (getParLanguage(bparams)->babel() != "hebrew") {
1512                         os << "\\end{flushleft}";
1513                         column = 15;
1514                 } else {
1515                         os << "\\end{flushright}";
1516                         column = 16;
1517                 }
1518                 break;
1519         case LYX_ALIGN_RIGHT:
1520                 if (getParLanguage(bparams)->babel() != "hebrew") {
1521                         os << "\\end{flushright}";
1522                         column+= 16;
1523                 } else {
1524                         os << "\\end{flushleft}";
1525                         column = 15;
1526                 }
1527                 break;
1528         case LYX_ALIGN_CENTER:
1529                 os << "\\end{center}";
1530                 column = 12;
1531                 break;
1532         }
1533         return column;
1534 }
1535
1536
1537 // This one spits out the text of the paragraph
1538 bool Paragraph::simpleTeXOnePar(Buffer const * buf,
1539                                 BufferParams const & bparams,
1540                                 ostream & os, TexRow & texrow,
1541                                 bool moving_arg)
1542 {
1543         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
1544
1545         bool return_value = false;
1546
1547         LyXLayout style;
1548         
1549         // well we have to check if we are in an inset with unlimited
1550         // lenght (all in one row) if that is true then we don't allow
1551         // any special options in the paragraph and also we don't allow
1552         // any environment other then "Standard" to be valid!
1553         bool asdefault =
1554                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
1555
1556         if (asdefault) {
1557                 style = textclasslist.Style(bparams.textclass, 0);
1558         } else {
1559                 style = textclasslist.Style(bparams.textclass, layout);
1560         }
1561         
1562         LyXFont basefont;
1563
1564         // Maybe we have to create a optional argument.
1565         pos_type main_body;
1566         if (style.labeltype != LABEL_MANUAL)
1567                 main_body = 0;
1568         else
1569                 main_body = beginningOfMainBody();
1570
1571         int column = 0;
1572
1573         if (main_body > 0) {
1574                 os << '[';
1575                 ++column;
1576                 basefont = getLabelFont(bparams);
1577         } else {
1578                 basefont = getLayoutFont(bparams);
1579         }
1580
1581         moving_arg |= style.needprotect;
1582  
1583         // Which font is currently active?
1584         LyXFont running_font(basefont);
1585         // Do we have an open font change?
1586         bool open_font = false;
1587
1588         texrow.start(this, 0);
1589
1590         // if the paragraph is empty, the loop will not be entered at all
1591         if (!size()) {
1592                 if (style.isCommand()) {
1593                         os << '{';
1594                         ++column;
1595                 }
1596                 if (!asdefault)
1597                         column += startTeXParParams(bparams, os);
1598
1599         }
1600
1601         for (pos_type i = 0; i < size(); ++i) {
1602                 ++column;
1603                 // First char in paragraph or after label?
1604                 if (i == main_body) {
1605                         if (main_body > 0) {
1606                                 if (open_font) {
1607                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1608                                         open_font = false;
1609                                 }
1610                                 basefont = getLayoutFont(bparams);
1611                                 running_font = basefont;
1612                                 os << ']';
1613                                 ++column;
1614                         }
1615                         if (style.isCommand()) {
1616                                 os << '{';
1617                                 ++column;
1618                         }
1619                         
1620                         if (!asdefault)
1621                                 column += startTeXParParams(bparams, os);
1622                 }
1623                 
1624                 value_type c = getChar(i);
1625
1626                 // Fully instantiated font
1627                 LyXFont font = getFont(bparams, i);
1628
1629                 LyXFont const & last_font = running_font;
1630
1631                 // Spaces at end of font change are simulated to be
1632                 // outside font change, i.e. we write "\textXX{text} "
1633                 // rather than "\textXX{text }". (Asger)
1634                 if (open_font && c == ' ' && i <= size() - 2) {
1635                         LyXFont const & next_font = getFont(bparams, i + 1);
1636                         if (next_font != running_font
1637                             && next_font != font) {
1638                                 font = next_font;
1639                         }
1640                 }
1641                 
1642                 // We end font definition before blanks
1643                 if (font != running_font && open_font) {
1644                         column += running_font.latexWriteEndChanges(os,
1645                                                                     basefont,
1646                                                                     (i == main_body-1) ? basefont : font);
1647                         running_font = basefont;
1648                         open_font = false;
1649                 }
1650
1651                 // Blanks are printed before start of fontswitch
1652                 if (c == ' ') {
1653                         // Do not print the separation of the optional argument
1654                         if (i != main_body - 1) {
1655                                 pimpl_->simpleTeXBlanks(os, texrow, i,
1656                                                        column, font, style);
1657                         }
1658                 }
1659
1660                 // Do we need to change font?
1661                 if (font != running_font && i != main_body - 1) {
1662                         column += font.latexWriteStartChanges(os, basefont,
1663                                                               last_font);
1664                         running_font = font;
1665                         open_font = true;
1666                 }
1667
1668                 if (c == Paragraph::META_NEWLINE) {
1669                         // newlines are handled differently here than
1670                         // the default in SimpleTeXSpecialChars().
1671                         if (!style.newline_allowed) {
1672                                 os << '\n';
1673                         } else {
1674                                 if (open_font) {
1675                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1676                                         open_font = false;
1677                                 }
1678                                 basefont = getLayoutFont(bparams);
1679                                 running_font = basefont;
1680                                 if (font.family() == 
1681                                     LyXFont::TYPEWRITER_FAMILY) {
1682                                         os << "~";
1683                                 }
1684                                 if (moving_arg)
1685                                         os << "\\protect ";
1686                                 os << "\\\\\n";
1687                         }
1688                         texrow.newline();
1689                         texrow.start(this, i + 1);
1690                         column = 0;
1691                 } else {
1692                         pimpl_->simpleTeXSpecialChars(buf, bparams,
1693                                                       os, texrow, moving_arg,
1694                                                       font, running_font, 
1695                                                       basefont, open_font, 
1696                                                       style, i, column, c);
1697                 }
1698         }
1699
1700         // If we have an open font definition, we have to close it
1701         if (open_font) {
1702 #ifdef FIXED_LANGUAGE_END_DETECTION
1703                 if (next_) {
1704                         running_font
1705                                 .latexWriteEndChanges(os, basefont,
1706                                                       next_->getFont(bparams,
1707                                                       0));
1708                 } else {
1709                         running_font.latexWriteEndChanges(os, basefont,
1710                                                           basefont);
1711                 }
1712 #else
1713 #ifdef WITH_WARNINGS
1714 //#warning For now we ALWAYS have to close the foreign font settings if they are
1715 //#warning there as we start another \selectlanguage with the next paragraph if
1716 //#warning we are in need of this. This should be fixed sometime (Jug)
1717 #endif
1718                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1719 #endif
1720         }
1721
1722         // Needed if there is an optional argument but no contents.
1723         if (main_body > 0 && main_body == size()) {
1724                 os << "]~";
1725                 return_value = false;
1726         }
1727
1728         if (!asdefault) {
1729                 column += endTeXParParams(bparams, os);
1730         }
1731
1732         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1733         return return_value;
1734 }
1735
1736
1737 bool Paragraph::sgmlConvertChar(char c, string & sgml_string)
1738 {
1739         bool retval = false;
1740         switch (c) {
1741         case Paragraph::META_HFILL:
1742                 sgml_string.erase();
1743                 break;
1744         case Paragraph::META_NEWLINE:
1745                 sgml_string = '\n';
1746                 break;
1747         case '&': 
1748                 sgml_string = "&amp;";
1749                 break;
1750         case '<': 
1751                 sgml_string = "&lt;"; 
1752                 break;
1753         case '>':
1754                 sgml_string = "&gt;"; 
1755                 break;
1756         case '$': 
1757                 sgml_string = "&dollar;"; 
1758                 break;
1759         case '#': 
1760                 sgml_string = "&num;";
1761                 break;
1762         case '%': 
1763                 sgml_string = "&percnt;";
1764                 break;
1765         case '[': 
1766                 sgml_string = "&lsqb;";
1767                 break;
1768         case ']': 
1769                 sgml_string = "&rsqb;";
1770                 break;
1771         case '{': 
1772                 sgml_string = "&lcub;";
1773                 break;
1774         case '}': 
1775                 sgml_string = "&rcub;";
1776                 break;
1777         case '~': 
1778                 sgml_string = "&tilde;";
1779                 break;
1780         case '"': 
1781                 sgml_string = "&quot;";
1782                 break;
1783         case '\\': 
1784                 sgml_string = "&bsol;";
1785                 break;
1786         case ' ':
1787                 retval = true;
1788                 sgml_string = ' ';
1789                 break;
1790         case '\0': // Ignore :-)
1791                 sgml_string.erase();
1792                 break;
1793         default:
1794                 sgml_string = c;
1795                 break;
1796         }
1797         return retval;
1798 }
1799
1800
1801 Paragraph * Paragraph::TeXEnvironment(Buffer const * buf,
1802                                             BufferParams const & bparams,
1803                                             ostream & os, TexRow & texrow)
1804 {
1805         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << this << endl;
1806
1807         LyXLayout const & style =
1808                 textclasslist.Style(bparams.textclass,
1809                                     layout);
1810
1811         Language const * language = getParLanguage(bparams);
1812         Language const * doc_language = bparams.language;
1813         Language const * previous_language = previous_
1814                 ? previous_->getParLanguage(bparams) : doc_language;
1815         if (language->babel() != previous_language->babel()) {
1816
1817                 if (!lyxrc.language_command_end.empty() &&
1818                     previous_language->babel() != doc_language->babel()) {
1819                         os << subst(lyxrc.language_command_end, "$$lang",
1820                                     previous_language->babel())
1821                            << endl;
1822                         texrow.newline();
1823                 }
1824
1825                 if (lyxrc.language_command_end.empty() ||
1826                     language->babel() != doc_language->babel()) {
1827                         os << subst(lyxrc.language_command_begin, "$$lang",
1828                                     language->babel())
1829                            << endl;
1830                         texrow.newline();
1831                 }
1832         }
1833
1834         if (style.isEnvironment()) {
1835                 if (style.latextype == LATEX_LIST_ENVIRONMENT) {
1836                         os << "\\begin{" << style.latexname() << "}{"
1837                            << params().labelWidthString() << "}\n";
1838                 } else if (style.labeltype == LABEL_BIBLIO) {
1839                         // ale970405
1840                         os << "\\begin{" << style.latexname() << "}{"
1841                            <<  bibitemWidest(buf)
1842                            << "}\n";
1843                 } else if (style.latextype == LATEX_ITEM_ENVIRONMENT) {
1844                         os << "\\begin{" << style.latexname() << '}'
1845                            << style.latexparam() << '\n';
1846                 } else 
1847                         os << "\\begin{" << style.latexname() << '}'
1848                            << style.latexparam() << '\n';
1849                 texrow.newline();
1850         }
1851         Paragraph * par = this;
1852         do {
1853                 par = par->TeXOnePar(buf, bparams, os, texrow, false);
1854
1855                 if (par && par->params().depth() > params().depth()) {
1856                         if (textclasslist.Style(bparams.textclass,
1857                                                 par->layout).isParagraph()
1858                             // Thinko!
1859                             // How to handle this? (Lgb)
1860                             //&& !suffixIs(os, "\n\n")
1861                                 ) {
1862                                 // There should be at least one '\n' already
1863                                 // but we need there to be two for Standard 
1864                                 // paragraphs that are depth-increment'ed to be
1865                                 // output correctly.  However, tables can
1866                                 // also be paragraphs so don't adjust them.
1867                                 // ARRae
1868                                 // Thinkee:
1869                                 // Will it ever harm to have one '\n' too
1870                                 // many? i.e. that we sometimes will have
1871                                 // three in a row. (Lgb)
1872                                 os << '\n';
1873                                 texrow.newline();
1874                         }
1875                         par = par->pimpl_->TeXDeeper(buf, bparams, os, texrow);
1876                 }
1877         } while (par
1878                  && par->layout == layout
1879                  && par->params().depth() == params().depth());
1880  
1881         if (style.isEnvironment()) {
1882                 os << "\\end{" << style.latexname() << "}\n";
1883                 texrow.newline();
1884         }
1885
1886         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << par << endl;
1887         return par;  // ale970302
1888 }
1889
1890
1891 bool Paragraph::isHfill(pos_type pos) const
1892 {
1893         return IsHfillChar(getChar(pos));
1894 }
1895
1896
1897 bool Paragraph::isInset(pos_type pos) const
1898 {
1899         return IsInsetChar(getChar(pos));
1900 }
1901
1902
1903 bool Paragraph::isNewline(pos_type pos) const
1904 {
1905         return pos >= 0 && IsNewlineChar(getChar(pos));
1906 }
1907
1908
1909 bool Paragraph::isSeparator(pos_type pos) const
1910 {
1911         return IsSeparatorChar(getChar(pos));
1912 }
1913
1914
1915 bool Paragraph::isLineSeparator(pos_type pos) const
1916 {
1917         value_type const c = getChar(pos);
1918         return IsLineSeparatorChar(c)
1919                 || (IsInsetChar(c) && getInset(pos)->isLineSeparator());
1920 }
1921
1922
1923 bool Paragraph::isKomma(pos_type pos) const
1924 {
1925         return IsKommaChar(getChar(pos));
1926 }
1927
1928
1929 /// Used by the spellchecker
1930 bool Paragraph::isLetter(pos_type pos) const
1931 {
1932         value_type const c = getChar(pos);
1933         if (IsLetterChar(c))
1934                 return true;
1935         if (isInset(pos)) 
1936                 return getInset(pos)->isLetter();
1937         // We want to pass the ' and escape chars to ispell
1938         string const extra = lyxrc.isp_esc_chars + '\'';
1939         return contains(extra, c);
1940 }
1941  
1942  
1943 bool Paragraph::isWord(pos_type pos) const
1944 {
1945         return IsWordChar(getChar(pos)) ;
1946 }
1947
1948
1949 Language const *
1950 Paragraph::getParLanguage(BufferParams const & bparams) const 
1951 {
1952         if (size() > 0) {
1953 #ifndef INHERIT_LANGUAGE
1954                 return getFirstFontSettings().language();
1955 #else
1956                 Language const * lang = getFirstFontSettings().language();
1957 #ifdef WITH_WARNINGS
1958 #warning We should make this somewhat better, any ideas? (Jug)
1959 #endif
1960                 if (lang == inherit_language || lang == ignore_language)
1961                         lang = bparams.language;
1962                 return lang;
1963 #endif
1964         } else if (previous_)
1965                 return previous_->getParLanguage(bparams);
1966         else
1967                 return bparams.language;
1968 }
1969
1970
1971 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1972 {
1973         return lyxrc.rtl_support
1974                 && getParLanguage(bparams)->RightToLeft();
1975 }
1976
1977
1978 void Paragraph::changeLanguage(BufferParams const & bparams,
1979                                   Language const * from, Language const * to)
1980 {
1981         for (pos_type i = 0; i < size(); ++i) {
1982                 LyXFont font = getFontSettings(bparams, i);
1983                 if (font.language() == from) {
1984                         font.setLanguage(to);
1985                         setFont(i, font);
1986                 }
1987         }
1988 }
1989
1990
1991 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1992 {
1993         Language const * doc_language = bparams.language;
1994         for (Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1995              cit != pimpl_->fontlist.end(); ++cit)
1996                 if (cit->font().language() != ignore_language &&
1997                     cit->font().language() != latex_language &&
1998 #ifdef INHERIT_LANGUAGE
1999                         cit->font().language() != inherit_language &&
2000 #endif
2001                         cit->font().language() != doc_language)
2002                         return true;
2003         return false;
2004 }
2005
2006
2007 // Convert the paragraph to a string.
2008 // Used for building the table of contents
2009 string const Paragraph::asString(Buffer const * buffer, bool label)
2010 {
2011         BufferParams const & bparams = buffer->params;
2012         string s;
2013         if (label && !params().labelString().empty())
2014                 s += params().labelString() + ' ';
2015         string::size_type const len = s.size();
2016
2017         for (pos_type i = 0; i < size(); ++i) {
2018                 value_type c = getChar(i);
2019                 if (IsPrintable(c))
2020                         s += c;
2021                 else if (c == META_INSET &&
2022                          getInset(i)->lyxCode() == Inset::MATH_CODE) {
2023                         ostringstream ost;
2024                         getInset(i)->ascii(buffer, ost);
2025                         s += subst(ost.str().c_str(),'\n',' ');
2026                 }
2027         }
2028
2029         if (isRightToLeftPar(bparams))
2030                 reverse(s.begin() + len,s.end());
2031
2032         return s;
2033 }
2034
2035
2036 string const Paragraph::asString(Buffer const * buffer, 
2037                                  pos_type beg, pos_type end, bool label)
2038 {
2039         ostringstream ost;
2040
2041         if (beg == 0 && label && !params().labelString().empty())
2042                 ost << params().labelString() << ' ';
2043
2044         for (pos_type i = beg; i < end; ++i) {
2045                 value_type const c = getUChar(buffer->params, i);
2046                 if (IsPrintable(c))
2047                         ost << c;
2048                 else if (c == META_NEWLINE)
2049                         ost << '\n';
2050                 else if (c == META_HFILL)
2051                         ost << '\t'; 
2052                 else if (c == META_INSET) {
2053                         getInset(i)->ascii(buffer, ost);
2054                 }
2055         }
2056
2057         return ost.str().c_str();
2058 }
2059
2060
2061 void Paragraph::setInsetOwner(Inset * i)
2062 {
2063         pimpl_->inset_owner = i;
2064         for (InsetList::const_iterator cit = insetlist.begin();
2065              cit != insetlist.end(); ++cit) {
2066                 if (cit->inset)
2067                         cit->inset->setOwner(i);
2068         }
2069 }
2070
2071
2072 void Paragraph::deleteInsetsLyXText(BufferView * bv)
2073 {
2074         // then the insets
2075         for (InsetList::const_iterator cit = insetlist.begin();
2076              cit != insetlist.end(); ++cit) {
2077                 if (cit->inset) {
2078                         if (cit->inset->isTextInset()) {
2079                                 static_cast<UpdatableInset *>
2080                                         (cit->inset)->deleteLyXText(bv, true);
2081                         }
2082                 }
2083         }
2084 }
2085
2086
2087 void Paragraph::resizeInsetsLyXText(BufferView * bv)
2088 {
2089         // then the insets
2090         for (InsetList::const_iterator cit = insetlist.begin();
2091              cit != insetlist.end(); ++cit)
2092         {
2093                 if (cit->inset) {
2094                         if (cit->inset->isTextInset()) {
2095                                 static_cast<UpdatableInset *>
2096                                         (cit->inset)->resizeLyXText(bv, true);
2097                         }
2098                 }
2099         }
2100 }
2101
2102
2103 void Paragraph::setContentsFromPar(Paragraph * par)
2104 {
2105         pimpl_->setContentsFromPar(par);
2106 }
2107
2108
2109 lyx::pos_type Paragraph::size() const
2110 {
2111         return pimpl_->size();
2112 }
2113
2114
2115 Paragraph::value_type Paragraph::getChar(pos_type pos) const
2116 {
2117         return pimpl_->getChar(pos);
2118 }
2119
2120
2121 int Paragraph::id() const
2122 {
2123         return pimpl_->id_;
2124 }
2125
2126
2127 void  Paragraph::id(int id_arg)
2128 {
2129         pimpl_->id_ = id_arg;
2130 }
2131
2132
2133 layout_type Paragraph::getLayout() const
2134 {
2135         return layout;
2136 }
2137
2138
2139 bool Paragraph::isFirstInSequence() const
2140 {
2141         Paragraph const * dhook = depthHook(getDepth());
2142         return (dhook == this
2143                 || dhook->getLayout() != getLayout()
2144                 || dhook->getDepth() != getDepth());
2145 }
2146
2147
2148 Inset * Paragraph::inInset() const
2149 {
2150         return pimpl_->inset_owner;
2151 }
2152
2153
2154 void Paragraph::clearContents()
2155 {
2156         pimpl_->clear();
2157 }
2158
2159
2160 void Paragraph::setCounter(int i, int v)
2161 {
2162         pimpl_->counter_[i] = v;
2163 }
2164
2165
2166 int Paragraph::getCounter(int i) const
2167 {
2168         return pimpl_->counter_[i];
2169 }
2170
2171
2172 void Paragraph::incCounter(int i)
2173 {
2174         pimpl_->counter_[i]++;
2175 }
2176
2177
2178 void Paragraph::setChar(pos_type pos, value_type c)
2179 {
2180         pimpl_->setChar(pos, c);
2181 }
2182
2183
2184 Paragraph::inset_iterator::inset_iterator(Paragraph::InsetList::iterator const & iter)
2185  : it(iter) 
2186 {}
2187
2188
2189 Paragraph::inset_iterator Paragraph::inset_iterator_begin()
2190 {
2191         return inset_iterator(insetlist.begin());
2192 }
2193
2194
2195 Paragraph::inset_iterator Paragraph::inset_iterator_end()
2196 {
2197         return inset_iterator(insetlist.end());
2198 }
2199
2200
2201 ParagraphParameters & Paragraph::params()
2202 {
2203         return pimpl_->params;
2204 }
2205
2206
2207 ParagraphParameters const & Paragraph::params() const
2208 {
2209         return pimpl_->params;
2210 }
2211
2212
2213 Paragraph * Paragraph::getParFromID(int id) const
2214 {
2215         return pimpl_->getParFromID(id);
2216 }
2217
2218
2219 bool Paragraph::isFreeSpacing() const
2220 {
2221         // for now we just need this, later should we need this in some
2222         // other way we can always add a function to Inset::() too.
2223         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
2224                 return (pimpl_->inset_owner->owner()->lyxCode() == Inset::ERT_CODE);
2225         return false;
2226 }