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