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