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