]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
6c904bf92daf4ba6d893ac38410fba41adfd5197
[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 void Paragraph::setFont(Paragraph::size_type pos,
711                            LyXFont const & font)
712 {
713         lyx::Assert(pos <= size());
714
715         // First, reduce font against layout/label font
716         // Update: The SetCharFont() routine in text2.C already
717         // reduces font, so we don't need to do that here. (Asger)
718         // No need to simplify this because it will disappear
719         // in a new kernel. (Asger)
720         // Next search font table
721
722         Pimpl::FontTable search_font(pos, LyXFont());
723         Pimpl::FontList::iterator it = lower_bound(pimpl_->fontlist.begin(),
724                                             pimpl_->fontlist.end(),
725                                             search_font, Pimpl::matchFT());
726         unsigned int i = it - pimpl_->fontlist.begin();
727         bool notfound = it == pimpl_->fontlist.end();
728
729         if (!notfound && pimpl_->fontlist[i].font() == font)
730                 return;
731
732         bool begin = pos == 0 || notfound ||
733                 (i > 0 && pimpl_->fontlist[i-1].pos() == pos - 1);
734         // Is position pos is a beginning of a font block?
735         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
736         // Is position pos is the end of a font block?
737         if (begin && end) { // A single char block
738                 if (i + 1 < pimpl_->fontlist.size() &&
739                     pimpl_->fontlist[i + 1].font() == font) {
740                         // Merge the singleton block with the next block
741                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
742                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
743                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i-1);
744                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
745                         // Merge the singleton block with the previous block
746                         pimpl_->fontlist[i - 1].pos(pos);
747                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
748                 } else
749                         pimpl_->fontlist[i].font(font);
750         } else if (begin) {
751                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
752                         pimpl_->fontlist[i - 1].pos(pos);
753                 else
754                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
755                                         Pimpl::FontTable(pos, font));
756         } else if (end) {
757                 pimpl_->fontlist[i].pos(pos - 1);
758                 if (!(i + 1 < pimpl_->fontlist.size() &&
759                       pimpl_->fontlist[i + 1].font() == font))
760                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
761                                         Pimpl::FontTable(pos, font));
762         } else { // The general case. The block is splitted into 3 blocks
763                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i, 
764                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
765                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
766                                 Pimpl::FontTable(pos, font));
767         }
768 }
769
770
771
772 void Paragraph::next(Paragraph * p)
773 {
774         next_ = p;
775 }
776
777
778 // This function is able to hide closed footnotes.
779 Paragraph * Paragraph::next()
780 {
781         return next_;
782 }
783
784
785 Paragraph const * Paragraph::next() const
786 {
787         return next_;
788 }
789
790
791 void Paragraph::previous(Paragraph * p)
792 {
793         previous_ = p;
794 }
795
796
797 // This function is able to hide closed footnotes.
798 Paragraph * Paragraph::previous()
799 {
800         return previous_;
801 }
802
803
804 // This function is able to hide closed footnotes.
805 Paragraph const * Paragraph::previous() const
806 {
807         return previous_;
808 }
809
810
811 void Paragraph::breakParagraph(BufferParams const & bparams,
812                                   Paragraph::size_type pos,
813                                   int flag)
814 {
815         // create a new paragraph
816         Paragraph * tmp = new Paragraph(this);
817         // remember to set the inset_owner
818         tmp->setInsetOwner(inInset());
819         
820         // this is an idea for a more userfriendly layout handling, I will
821         // see what the users say
822         
823         // layout stays the same with latex-environments
824         if (flag) {
825                 tmp->setOnlyLayout(layout);
826                 tmp->setLabelWidthString(params().labelWidthString());
827         }
828         
829         if (size() > pos || !size() || flag == 2) {
830                 tmp->setOnlyLayout(layout);
831                 tmp->params().align(params().align());
832                 tmp->setLabelWidthString(params().labelWidthString());
833                 
834                 tmp->params().lineBottom(params().lineBottom());
835                 params().lineBottom(false);
836                 tmp->params().pagebreakBottom(params().pagebreakBottom());
837                 params().pagebreakBottom(false);
838                 tmp->params().spaceBottom(params().spaceBottom());
839                 params().spaceBottom(VSpace(VSpace::NONE));
840                 
841                 tmp->params().depth(params().depth());
842                 tmp->params().noindent(params().noindent());
843                 
844                 // copy everything behind the break-position
845                 // to the new paragraph
846                 size_type pos_end = pimpl_->size() - 1;
847                 size_type i = pos;
848                 size_type j = pos;
849                 for (; i <= pos_end; ++i) {
850                         cutIntoMinibuffer(bparams, i);
851                         if (tmp->insertFromMinibuffer(j - pos))
852                                 ++j;
853                 }
854                 for (i = pos_end; i >= pos; --i) {
855                         erase(i);
856                 }
857         }
858         
859         // just an idea of me
860         if (!pos) {
861                 tmp->params().lineTop(params().lineTop());
862                 tmp->params().pagebreakTop(params().pagebreakTop());
863                 tmp->params().spaceTop(params().spaceTop());
864                 tmp->bibkey = bibkey;
865                 clear();
866                 // layout stays the same with latex-environments
867                 if (flag) {
868                         setOnlyLayout(tmp->layout);
869                         setLabelWidthString(tmp->params().labelWidthString());
870                         params().depth(tmp->params().depth());
871                 }
872         }
873 }
874         
875
876 void Paragraph::makeSameLayout(Paragraph const * par)
877 {
878         layout = par->layout;
879         // move to pimpl?
880         params() = par->params();
881 }
882
883
884 int Paragraph::stripLeadingSpaces(LyXTextClassList::size_type tclass) 
885 {
886         if (textclasslist.Style(tclass, getLayout()).free_spacing)
887                 return 0;
888         
889         int i = 0;
890         while (size()
891                && (isNewline(0) || isLineSeparator(0))){
892                 erase(0);
893                 ++i;
894         }
895
896         return i;
897 }
898
899
900 bool Paragraph::hasSameLayout(Paragraph const * par) const
901 {
902         return 
903                 par->layout == layout &&
904                 params().sameLayout(par->params());
905 }
906
907
908 void Paragraph::breakParagraphConservative(BufferParams const & bparams,
909                                            Paragraph::size_type pos)
910 {
911         // create a new paragraph
912         Paragraph * tmp = new Paragraph(this);
913         tmp->makeSameLayout(this);
914
915         // When can pos > Last()?
916         // I guess pos == Last() is possible.
917         if (size() > pos) {
918                 // copy everything behind the break-position to the new
919                 // paragraph
920                 size_type pos_end = pimpl_->size() - 1;
921
922                 //size_type i = pos;
923                 //size_type j = pos;
924                 for (size_type i = pos, j = pos; i <= pos_end; ++i) {
925                         cutIntoMinibuffer(bparams, i);
926                         if (tmp->insertFromMinibuffer(j - pos))
927                                 ++j;
928                 }
929                 
930                 for (size_type k = pos_end; k >= pos; --k) {
931                         erase(k);
932                 }
933         }
934 }
935    
936
937 // Be carefull, this does not make any check at all.
938 // This method has wrong name, it combined this par with the next par.
939 // In that sense it is the reverse of break paragraph. (Lgb)
940 void Paragraph::pasteParagraph(BufferParams const & bparams)
941 {
942         // copy the next paragraph to this one
943         Paragraph * the_next = next();
944    
945         // first the DTP-stuff
946         params().lineBottom(the_next->params().lineBottom());
947         params().spaceBottom(the_next->params().spaceBottom());
948         params().pagebreakBottom(the_next->params().pagebreakBottom());
949
950         size_type pos_end = the_next->pimpl_->size() - 1;
951         size_type pos_insert = size();
952
953         // ok, now copy the paragraph
954         for (size_type i = 0, j = 0; i <= pos_end; ++i) {
955                 the_next->cutIntoMinibuffer(bparams, i);
956                 if (insertFromMinibuffer(pos_insert + j))
957                         ++j;
958         }
959    
960         // delete the next paragraph
961         Paragraph * ppar = the_next->previous_;
962         Paragraph * npar = the_next->next_;
963         delete the_next;
964         ppar->next(npar);
965 }
966
967
968 int Paragraph::getEndLabel(BufferParams const & bparams) const
969 {
970         Paragraph const * par = this;
971         depth_type par_depth = getDepth();
972         while (par) {
973                 LyXTextClass::LayoutList::size_type layout = par->getLayout();
974                 int const endlabeltype =
975                         textclasslist.Style(bparams.textclass,
976                                             layout).endlabeltype;
977                 if (endlabeltype != END_LABEL_NO_LABEL) {
978                         if (!next_)
979                                 return endlabeltype;
980
981                         depth_type const next_depth = next_->getDepth();
982                         if (par_depth > next_depth ||
983                             (par_depth == next_depth
984                              && layout != next_->getLayout()))
985                                 return endlabeltype;
986                         break;
987                 }
988                 if (par_depth == 0)
989                         break;
990                 par = par->outerHook();
991                 if (par)
992                         par_depth = par->getDepth();
993         }
994         return END_LABEL_NO_LABEL;
995 }
996
997
998 Paragraph::depth_type Paragraph::getDepth() const
999 {
1000         return params().depth();
1001 }
1002
1003
1004 char Paragraph::getAlign() const
1005 {
1006         return params().align();
1007 }
1008
1009
1010 string const & Paragraph::getLabelstring() const
1011 {
1012         return params().labelString();
1013 }
1014
1015
1016 int Paragraph::getFirstCounter(int i) const
1017 {
1018         return pimpl_->counter_[i];
1019 }
1020
1021
1022 // the next two functions are for the manual labels
1023 string const Paragraph::getLabelWidthString() const
1024 {
1025         if (!params().labelWidthString().empty())
1026                 return params().labelWidthString();
1027         else
1028                 return _("Senseless with this layout!");
1029 }
1030
1031
1032 void Paragraph::setLabelWidthString(string const & s)
1033 {
1034         params().labelWidthString(s);
1035 }
1036
1037
1038 void Paragraph::setOnlyLayout(LyXTextClass::size_type new_layout)
1039 {
1040         layout = new_layout;
1041 }
1042
1043
1044 void Paragraph::setLayout(LyXTextClass::size_type new_layout)
1045 {
1046         layout = new_layout;
1047         params().labelWidthString(string());
1048         params().align(LYX_ALIGN_LAYOUT);
1049         params().spaceTop(VSpace(VSpace::NONE));
1050         params().spaceBottom(VSpace(VSpace::NONE));
1051         params().spacing(Spacing(Spacing::Default));
1052 }
1053
1054
1055 // if the layout of a paragraph contains a manual label, the beginning of the 
1056 // main body is the beginning of the second word. This is what the par-
1057 // function returns. If the layout does not contain a label, the main
1058 // body always starts with position 0. This differentiation is necessary,
1059 // because there cannot be a newline or a blank <= the beginning of the 
1060 // main body in TeX.
1061
1062 int Paragraph::beginningOfMainBody() const
1063 {
1064         // Unroll the first two cycles of the loop
1065         // and remember the previous character to
1066         // remove unnecessary GetChar() calls
1067         size_type i = 0;
1068         if (i < size()
1069             && getChar(i) != Paragraph::META_NEWLINE) {
1070                 ++i;
1071                 char previous_char = 0;
1072                 char temp = 0; 
1073                 if (i < size()
1074                     && (previous_char = getChar(i)) != Paragraph::META_NEWLINE) {
1075                         // Yes, this  ^ is supposed to be "= " not "=="
1076                         ++i;
1077                         while (i < size()
1078                                && previous_char != ' '
1079                                && (temp = getChar(i)) != Paragraph::META_NEWLINE) {
1080                                 ++i;
1081                                 previous_char = temp;
1082                         }
1083                 }
1084         }
1085
1086         return i;
1087 }
1088
1089
1090 Paragraph * Paragraph::depthHook(depth_type depth)
1091 {
1092         Paragraph * newpar = this;
1093   
1094         do {
1095                 newpar = newpar->previous();
1096         } while (newpar && newpar->getDepth() > depth);
1097    
1098         if (!newpar) {
1099                 if (previous() || getDepth())
1100                         lyxerr << "ERROR (Paragraph::DepthHook): "
1101                                 "no hook." << endl;
1102                 newpar = this;
1103         }
1104
1105         return newpar;
1106 }
1107
1108
1109 Paragraph const * Paragraph::depthHook(depth_type depth) const
1110 {
1111         Paragraph const * newpar = this;
1112    
1113         do {
1114                 newpar = newpar->previous();
1115         } while (newpar && newpar->getDepth() > depth);
1116    
1117         if (!newpar) {
1118                 if (previous() || getDepth())
1119                         lyxerr << "ERROR (Paragraph::DepthHook): "
1120                                 "no hook." << endl;
1121                 newpar = this;
1122         }
1123
1124         return newpar;
1125 }
1126
1127 Paragraph * Paragraph::outerHook()
1128 {
1129         if(!getDepth())
1130                 return 0;
1131         return depthHook(depth_type(getDepth() - 1));
1132 }
1133
1134 Paragraph const * Paragraph::outerHook() const
1135 {
1136         if(!getDepth())
1137                 return 0;
1138         return depthHook(depth_type(getDepth() - 1));
1139 }
1140
1141 int Paragraph::autoDeleteInsets()
1142 {
1143         int count = 0;
1144         InsetList::size_type index = 0;
1145         while (index < insetlist.size()) {
1146                 if (insetlist[index].inset && insetlist[index].inset->autoDelete()) {
1147                         erase(insetlist[index].pos); 
1148                         // Erase() calls to insetlist.erase(&insetlist[index])
1149                         // so index shouldn't be increased.
1150                         ++count;
1151                 } else
1152                         ++index;
1153         }
1154         return count;
1155 }
1156
1157
1158 Paragraph::inset_iterator
1159 Paragraph::InsetIterator(Paragraph::size_type pos)
1160 {
1161         InsetTable search_inset(pos, 0);
1162         InsetList::iterator it = lower_bound(insetlist.begin(),
1163                                              insetlist.end(),
1164                                              search_inset, Pimpl::matchIT());
1165         return inset_iterator(it);
1166 }
1167
1168
1169 // returns -1 if inset not found
1170 int Paragraph::getPositionOfInset(Inset * inset) const
1171 {
1172         // Find the entry.
1173         for (InsetList::const_iterator cit = insetlist.begin();
1174              cit != insetlist.end(); ++cit) {
1175                 if (cit->inset == inset) {
1176                         return cit->pos;
1177                 }
1178         }
1179         if (inset == bibkey)
1180                 return 0;
1181
1182         return -1;
1183 }
1184
1185
1186 Paragraph * Paragraph::TeXOnePar(Buffer const * buf,
1187                                        BufferParams const & bparams,
1188                                        ostream & os, TexRow & texrow,
1189                                        bool moving_arg)
1190 {
1191         lyxerr[Debug::LATEX] << "TeXOnePar...     " << this << endl;
1192         LyXLayout const & style =
1193                 textclasslist.Style(bparams.textclass,
1194                                     layout);
1195
1196         bool further_blank_line = false;
1197
1198         if (params().startOfAppendix()) {
1199                 os << "\\appendix\n";
1200                 texrow.newline();
1201         }
1202
1203         if (!params().spacing().isDefault()
1204             && (!previous() || !previous()->hasSameLayout(this))) {
1205                 os << params().spacing().writeEnvirBegin() << "\n";
1206                 texrow.newline();
1207         }
1208         
1209         if (tex_code_break_column && style.isCommand()){
1210                 os << '\n';
1211                 texrow.newline();
1212         }
1213
1214         if (params().pagebreakTop()) {
1215                 os << "\\newpage";
1216                 further_blank_line = true;
1217         }
1218         if (params().spaceTop().kind() != VSpace::NONE) {
1219                 os << params().spaceTop().asLatexCommand(bparams);
1220                 further_blank_line = true;
1221         }
1222       
1223         if (params().lineTop()) {
1224                 os << "\\lyxline{\\" << getFont(bparams, 0).latexSize() << '}'
1225                    << "\\vspace{-1\\parskip}";
1226                 further_blank_line = true;
1227         }
1228
1229         if (further_blank_line){
1230                 os << '\n';
1231                 texrow.newline();
1232         }
1233
1234         Language const * language = getParLanguage(bparams);
1235         Language const * doc_language = bparams.language;
1236         Language const * previous_language = previous_
1237                 ? previous_->getParLanguage(bparams) : doc_language;
1238         if (language->babel() != doc_language->babel() &&
1239             language->babel() != previous_language->babel()) {
1240                 os << subst(lyxrc.language_command_begin, "$$lang",
1241                             language->babel())
1242                    << endl;
1243                 texrow.newline();
1244         }
1245
1246         if (bparams.inputenc == "auto" &&
1247             language->encoding() != previous_language->encoding()) {
1248                 os << "\\inputencoding{"
1249                    << language->encoding()->LatexName()
1250                    << "}" << endl;
1251                 texrow.newline();
1252         }
1253         
1254         switch (style.latextype) {
1255         case LATEX_COMMAND:
1256                 os << '\\'
1257                    << style.latexname()
1258                    << style.latexparam();
1259                 break;
1260         case LATEX_ITEM_ENVIRONMENT:
1261                 if (bibkey) {
1262                         bibkey->latex(buf, os, false, false);
1263                 } else
1264                         os << "\\item ";
1265                 break;
1266         case LATEX_LIST_ENVIRONMENT:
1267                 os << "\\item ";
1268                 break;
1269         default:
1270                 break;
1271         }
1272
1273         bool need_par = simpleTeXOnePar(buf, bparams, os, texrow, moving_arg);
1274  
1275         // Make sure that \\par is done with the font of the last
1276         // character if this has another size as the default.
1277         // This is necessary because LaTeX (and LyX on the screen)
1278         // calculates the space between the baselines according
1279         // to this font. (Matthias)
1280         //
1281         // Is this really needed ? (Dekel)
1282         // We do not need to use to change the font for the last paragraph
1283         // or for a command.
1284         LyXFont font = getFont(bparams, size() - 1);
1285
1286         bool is_command = textclasslist.Style(bparams.textclass,
1287                                               getLayout()).isCommand();
1288         if (style.resfont.size() != font.size() && next_ && !is_command) {
1289                 if (!need_par)
1290                         os << "{";
1291                 os << "\\" << font.latexSize() << " \\par}";
1292         } else if (need_par) {
1293                 os << "\\par}";
1294         } else if (is_command)
1295                 os << "}";
1296
1297         if (language->babel() != doc_language->babel() &&
1298             (!next_
1299              || next_->getParLanguage(bparams)->babel() != language->babel())) {
1300                 os << endl 
1301                    << subst(lyxrc.language_command_end, "$$lang",
1302                             doc_language->babel());
1303         }
1304         
1305         switch (style.latextype) {
1306         case LATEX_ITEM_ENVIRONMENT:
1307         case LATEX_LIST_ENVIRONMENT:
1308                 if (next_ && (params().depth() < next_->params().depth())) {
1309                         os << '\n';
1310                         texrow.newline();
1311                 }
1312                 break;
1313         case LATEX_ENVIRONMENT:
1314                 // if its the last paragraph of the current environment
1315                 // skip it otherwise fall through
1316                 if (next_
1317                     && (next_->layout != layout
1318                         || next_->params().depth() != params().depth()))
1319                         break;
1320         default:
1321                 // we don't need it for the last paragraph!!!
1322                 // or for tables in floats
1323                 //   -- effectively creates a \par where there isn't one which
1324                 //      breaks a \subfigure or \subtable.
1325                 if (next_) {
1326                         os << '\n';
1327                         texrow.newline();
1328                 }
1329         }
1330         
1331         further_blank_line = false;
1332         if (params().lineBottom()) {
1333                 os << "\\lyxline{\\" << getFont(bparams,
1334                                                 size() - 1).latexSize() << '}';
1335                 further_blank_line = true;
1336         }
1337
1338         if (params().spaceBottom().kind() != VSpace::NONE) {
1339                 os << params().spaceBottom().asLatexCommand(bparams);
1340                 further_blank_line = true;
1341         }
1342       
1343         if (params().pagebreakBottom()) {
1344                 os << "\\newpage";
1345                 further_blank_line = true;
1346         }
1347
1348         if (further_blank_line){
1349                 os << '\n';
1350                 texrow.newline();
1351         }
1352
1353         if (!params().spacing().isDefault()
1354             && (!next_ || !next_->hasSameLayout(this))) {
1355                 os << params().spacing().writeEnvirEnd() << "\n";
1356                 texrow.newline();
1357         }
1358         
1359         // we don't need it for the last paragraph!!!
1360         if (next_) {
1361                 os << '\n';
1362                 texrow.newline();
1363         }
1364
1365         lyxerr[Debug::LATEX] << "TeXOnePar...done " << next_ << endl;
1366         return next_;
1367 }
1368
1369
1370 // This one spits out the text of the paragraph
1371 bool Paragraph::simpleTeXOnePar(Buffer const * buf,
1372                                    BufferParams const & bparams,
1373                                    ostream & os, TexRow & texrow,
1374                                    bool moving_arg)
1375 {
1376         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
1377
1378         bool return_value = false;
1379
1380         LyXLayout const & style =
1381                 textclasslist.Style(bparams.textclass,
1382                                     getLayout());
1383         LyXFont basefont;
1384
1385         // Maybe we have to create a optional argument.
1386         size_type main_body;
1387         if (style.labeltype != LABEL_MANUAL)
1388                 main_body = 0;
1389         else
1390                 main_body = beginningOfMainBody();
1391
1392         int column = 0;
1393
1394         if (main_body > 0) {
1395                 os << '[';
1396                 ++column;
1397                 basefont = getFont(bparams, -2); // Get label font
1398         } else {
1399                 basefont = getFont(bparams, -1); // Get layout font
1400         }
1401
1402         if (main_body >= 0
1403             && !pimpl_->size()) {
1404                 if (style.isCommand()) {
1405                         os << '{';
1406                         ++column;
1407                 }
1408         }
1409
1410         moving_arg |= style.needprotect;
1411  
1412         // Which font is currently active?
1413         LyXFont running_font(basefont);
1414         // Do we have an open font change?
1415         bool open_font = false;
1416
1417         texrow.start(this, 0);
1418
1419         for (size_type i = 0; i < size(); ++i) {
1420                 ++column;
1421                 // First char in paragraph or after label?
1422                 if (i == main_body) {
1423                         if (main_body > 0) {
1424                                 if (open_font) {
1425                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1426                                         open_font = false;
1427                                 }
1428                                 basefont = getFont(bparams, -1); // Now use the layout font
1429                                 running_font = basefont;
1430                                 os << ']';
1431                                 ++column;
1432                         }
1433                         if (style.isCommand()) {
1434                                 os << '{';
1435                                 ++column;
1436                         }
1437
1438                         if (params().noindent()) {
1439                                 os << "\\noindent ";
1440                                 column += 10;
1441                         }
1442                         switch (params().align()) {
1443                         case LYX_ALIGN_NONE:
1444                         case LYX_ALIGN_BLOCK:
1445                         case LYX_ALIGN_LAYOUT:
1446                         case LYX_ALIGN_SPECIAL:
1447                                 break;
1448                         case LYX_ALIGN_LEFT:
1449                                 if (getParLanguage(bparams)->babel() != "hebrew") {
1450                                         os << "\\begin{flushleft}";
1451                                         column+= 17;
1452                                 } else {
1453                                         os << "\\begin{flushright}";
1454                                         column+= 18;
1455                                 }
1456                                 break;
1457                         case LYX_ALIGN_RIGHT:
1458                                 if (getParLanguage(bparams)->babel() != "hebrew") {
1459                                         os << "\\begin{flushright}";
1460                                         column+= 18;
1461                                 } else {
1462                                         os << "\\begin{flushleft}";
1463                                         column+= 17;
1464                                 }
1465                                 break;
1466                         case LYX_ALIGN_CENTER:
1467                                 os << "\\begin{center}";
1468                                 column+= 14;
1469                                 break;
1470                         }        
1471                 }
1472
1473                 value_type c = getChar(i);
1474
1475                 // Fully instantiated font
1476                 LyXFont font = getFont(bparams, i);
1477
1478                 LyXFont last_font = running_font;
1479
1480                 // Spaces at end of font change are simulated to be
1481                 // outside font change, i.e. we write "\textXX{text} "
1482                 // rather than "\textXX{text }". (Asger)
1483                 if (open_font && c == ' ' && i <= size() - 2 
1484                     && !getFont(bparams, i + 1).equalExceptLatex(running_font) 
1485                     && !getFont(bparams, i + 1).equalExceptLatex(font)) {
1486                         font = getFont(bparams, i + 1);
1487                 }
1488                 // We end font definition before blanks
1489                 if (!font.equalExceptLatex(running_font) && open_font) {
1490                         column += running_font.latexWriteEndChanges(os,
1491                                                                     basefont,
1492                                                                     (i == main_body-1) ? basefont : font);
1493                         running_font = basefont;
1494                         open_font = false;
1495                 }
1496
1497                 // Blanks are printed before start of fontswitch
1498                 if (c == ' ') {
1499                         // Do not print the separation of the optional argument
1500                         if (i != main_body - 1) {
1501                                 pimpl_->simpleTeXBlanks(os, texrow, i,
1502                                                        column, font, style);
1503                         }
1504                 }
1505
1506                 // Do we need to change font?
1507                 if (!font.equalExceptLatex(running_font)
1508                     && i != main_body-1) {
1509                         column += font.latexWriteStartChanges(os, basefont,
1510                                                               last_font);
1511                         running_font = font;
1512                         open_font = true;
1513                 }
1514
1515                 if (c == Paragraph::META_NEWLINE) {
1516                         // newlines are handled differently here than
1517                         // the default in SimpleTeXSpecialChars().
1518                         if (!style.newline_allowed
1519 #ifndef NO_LATEX
1520                             || font.latex() == LyXFont::ON
1521 #endif
1522                                 ) {
1523                                 os << '\n';
1524                         } else {
1525                                 if (open_font) {
1526                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1527                                         open_font = false;
1528                                 }
1529                                 basefont = getFont(bparams, -1);
1530                                 running_font = basefont;
1531                                 if (font.family() == 
1532                                     LyXFont::TYPEWRITER_FAMILY) {
1533                                         os << "~";
1534                                 }
1535                                 if (moving_arg)
1536                                         os << "\\protect ";
1537                                 os << "\\\\\n";
1538                         }
1539                         texrow.newline();
1540                         texrow.start(this, i + 1);
1541                         column = 0;
1542                 } else {
1543                         pimpl_->simpleTeXSpecialChars(buf, bparams,
1544                                                       os, texrow, moving_arg,
1545                                                       font, running_font, 
1546                                                       basefont, open_font, 
1547                                                       style, i, column, c);
1548                 }
1549         }
1550
1551         // If we have an open font definition, we have to close it
1552         if (open_font) {
1553                 if (next_) {
1554                         running_font
1555                                 .latexWriteEndChanges(os, basefont,
1556                                                       next_->getFont(bparams,
1557                                                                      0));
1558                 } else {
1559                         running_font.latexWriteEndChanges(os, basefont,
1560                                                           basefont);
1561                 }
1562         }
1563
1564         // Needed if there is an optional argument but no contents.
1565         if (main_body > 0 && main_body == size()) {
1566                 os << "]~";
1567                 return_value = false;
1568         }
1569
1570         switch (params().align()) {
1571         case LYX_ALIGN_NONE:
1572         case LYX_ALIGN_BLOCK:
1573         case LYX_ALIGN_LAYOUT:
1574         case LYX_ALIGN_SPECIAL:
1575                 break;
1576         case LYX_ALIGN_LEFT:
1577                 if (getParLanguage(bparams)->babel() != "hebrew") {
1578                         os << "\\end{flushleft}";
1579                         column+= 15;
1580                 } else {
1581                         os << "\\end{flushright}";
1582                         column+= 16;
1583                 }
1584                 break;
1585         case LYX_ALIGN_RIGHT:
1586                 if (getParLanguage(bparams)->babel() != "hebrew") {
1587                         os << "\\end{flushright}";
1588                         column+= 16;
1589                 } else {
1590                         os << "\\end{flushleft}";
1591                         column+= 15;
1592                 }
1593                 break;
1594         case LYX_ALIGN_CENTER:
1595                 os << "\\end{center}";
1596                 column+= 12;
1597                 break;
1598         }        
1599
1600         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1601         return return_value;
1602 }
1603
1604
1605 bool Paragraph::linuxDocConvertChar(char c, string & sgml_string)
1606 {
1607         bool retval = false;
1608         switch (c) {
1609         case Paragraph::META_HFILL:
1610                 sgml_string.erase();
1611                 break;
1612         case Paragraph::META_NEWLINE:
1613                 sgml_string = '\n';
1614                 break;
1615         case '&': 
1616                 sgml_string = "&amp;";
1617                 break;
1618         case '<': 
1619                 sgml_string = "&lt;"; 
1620                 break;
1621         case '>':
1622                 sgml_string = "&gt;"; 
1623                 break;
1624         case '$': 
1625                 sgml_string = "&dollar;"; 
1626                 break;
1627         case '#': 
1628                 sgml_string = "&num;";
1629                 break;
1630         case '%': 
1631                 sgml_string = "&percnt;";
1632                 break;
1633         case '[': 
1634                 sgml_string = "&lsqb;";
1635                 break;
1636         case ']': 
1637                 sgml_string = "&rsqb;";
1638                 break;
1639         case '{': 
1640                 sgml_string = "&lcub;";
1641                 break;
1642         case '}': 
1643                 sgml_string = "&rcub;";
1644                 break;
1645         case '~': 
1646                 sgml_string = "&tilde;";
1647                 break;
1648         case '"': 
1649                 sgml_string = "&quot;";
1650                 break;
1651         case '\\': 
1652                 sgml_string = "&bsol;";
1653                 break;
1654         case ' ':
1655                 retval = true;
1656                 sgml_string = ' ';
1657                 break;
1658         case '\0': // Ignore :-)
1659                 sgml_string.erase();
1660                 break;
1661         default:
1662                 sgml_string = c;
1663                 break;
1664         }
1665         return retval;
1666 }
1667
1668
1669 Paragraph * Paragraph::TeXEnvironment(Buffer const * buf,
1670                                             BufferParams const & bparams,
1671                                             ostream & os, TexRow & texrow)
1672 {
1673         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << this << endl;
1674
1675         LyXLayout const & style =
1676                 textclasslist.Style(bparams.textclass,
1677                                     layout);
1678
1679         if (style.isEnvironment()){
1680                 if (style.latextype == LATEX_LIST_ENVIRONMENT) {
1681                         os << "\\begin{" << style.latexname() << "}{"
1682                            << params().labelWidthString() << "}\n";
1683                 } else if (style.labeltype == LABEL_BIBLIO) {
1684                         // ale970405
1685                         os << "\\begin{" << style.latexname() << "}{"
1686                            <<  bibitemWidest(buf)
1687                            << "}\n";
1688                 } else if (style.latextype == LATEX_ITEM_ENVIRONMENT) {
1689                         os << "\\begin{" << style.latexname() << '}'
1690                            << style.latexparam() << '\n';
1691                 } else 
1692                         os << "\\begin{" << style.latexname() << '}'
1693                            << style.latexparam() << '\n';
1694                 texrow.newline();
1695         }
1696         Paragraph * par = this;
1697         do {
1698                 par = par->TeXOnePar(buf, bparams,
1699                                      os, texrow, false);
1700
1701                 if (par && par->params().depth() > params().depth()) {
1702                         if (textclasslist.Style(bparams.textclass,
1703                                                 par->layout).isParagraph()
1704                             // Thinko!
1705                             // How to handle this? (Lgb)
1706                             //&& !suffixIs(os, "\n\n")
1707                                 ) {
1708                                 // There should be at least one '\n' already
1709                                 // but we need there to be two for Standard 
1710                                 // paragraphs that are depth-increment'ed to be
1711                                 // output correctly.  However, tables can
1712                                 // also be paragraphs so don't adjust them.
1713                                 // ARRae
1714                                 // Thinkee:
1715                                 // Will it ever harm to have one '\n' too
1716                                 // many? i.e. that we sometimes will have
1717                                 // three in a row. (Lgb)
1718                                 os << '\n';
1719                                 texrow.newline();
1720                         }
1721                         par = par->pimpl_->TeXDeeper(buf, bparams, os, texrow);
1722                 }
1723         } while (par
1724                  && par->layout == layout
1725                  && par->params().depth() == params().depth());
1726  
1727         if (style.isEnvironment()) {
1728                 os << "\\end{" << style.latexname() << "}\n";
1729         }
1730
1731         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << par << endl;
1732         return par;  // ale970302
1733 }
1734
1735
1736 bool Paragraph::isHfill(size_type pos) const
1737 {
1738         return IsHfillChar(getChar(pos));
1739 }
1740
1741
1742 bool Paragraph::isInset(size_type pos) const
1743 {
1744         return IsInsetChar(getChar(pos));
1745 }
1746
1747
1748 bool Paragraph::isNewline(size_type pos) const
1749 {
1750         return pos >= 0 && IsNewlineChar(getChar(pos));
1751 }
1752
1753
1754 bool Paragraph::isSeparator(size_type pos) const
1755 {
1756         return IsSeparatorChar(getChar(pos));
1757 }
1758
1759
1760 bool Paragraph::isLineSeparator(size_type pos) const
1761 {
1762         return IsLineSeparatorChar(getChar(pos));
1763 }
1764
1765
1766 bool Paragraph::isKomma(size_type pos) const
1767 {
1768         return IsKommaChar(getChar(pos));
1769 }
1770
1771
1772 /// Used by the spellchecker
1773 bool Paragraph::isLetter(Paragraph::size_type pos) const
1774 {
1775         value_type const c = getChar(pos);
1776         if (IsLetterChar(c))
1777                 return true;
1778         // '\0' is not a letter, allthough every string contains "" (below)
1779         if (c == '\0')
1780                 return false;
1781         // We want to pass the ' and escape chars to ispell
1782         string const extra = lyxrc.isp_esc_chars + '\'';
1783         char ch[2] = { c, 0 };
1784         return contains(extra, ch);
1785 }
1786  
1787  
1788 bool Paragraph::isWord(size_type pos ) const
1789 {
1790         return IsWordChar(getChar(pos)) ;
1791 }
1792
1793
1794 Language const *
1795 Paragraph::getParLanguage(BufferParams const & bparams) const 
1796 {
1797         if (size() > 0)
1798                 return getFirstFontSettings().language();
1799         else if (previous_)
1800                 return previous_->getParLanguage(bparams);
1801         else
1802                 return bparams.language;
1803 }
1804
1805
1806 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1807 {
1808         return lyxrc.rtl_support
1809                 && getParLanguage(bparams)->RightToLeft();
1810 }
1811
1812
1813 void Paragraph::changeLanguage(BufferParams const & bparams,
1814                                   Language const * from, Language const * to)
1815 {
1816         for (size_type i = 0; i < size(); ++i) {
1817                 LyXFont font = getFontSettings(bparams, i);
1818                 if (font.language() == from) {
1819                         font.setLanguage(to);
1820                         setFont(i, font);
1821                 }
1822         }
1823 }
1824
1825
1826 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1827 {
1828         Language const * doc_language = bparams.language;
1829         for (Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1830              cit != pimpl_->fontlist.end(); ++cit)
1831                 if (cit->font().language() != doc_language)
1832                         return true;
1833         return false;
1834 }
1835
1836
1837 // Convert the paragraph to a string.
1838 // Used for building the table of contents
1839 string const Paragraph::asString(Buffer const * buffer, bool label)
1840 {
1841         BufferParams const & bparams = buffer->params;
1842         string s;
1843         if (label && !params().labelString().empty())
1844                 s += params().labelString() + ' ';
1845         string::size_type const len = s.size();
1846
1847         for (Paragraph::size_type i = 0; i < size(); ++i) {
1848                 value_type c = getChar(i);
1849                 if (IsPrintable(c))
1850                         s += c;
1851                 else if (c == META_INSET &&
1852                          getInset(i)->lyxCode() == Inset::MATH_CODE) {
1853                         ostringstream ost;
1854                         getInset(i)->ascii(buffer, ost);
1855                         s += subst(ost.str().c_str(),'\n',' ');
1856                 }
1857         }
1858
1859         if (isRightToLeftPar(bparams))
1860                 reverse(s.begin() + len,s.end());
1861
1862         return s;
1863 }
1864
1865
1866 string const Paragraph::asString(Buffer const * buffer, 
1867                             Paragraph::size_type beg,
1868                             Paragraph::size_type end)
1869 {
1870         ostringstream ost;
1871
1872         if (beg == 0 && !params().labelString().empty())
1873                 ost << params().labelString() << ' ';
1874
1875         for (Paragraph::size_type i = beg; i < end; ++i) {
1876                 value_type const c = getUChar(buffer->params, i);
1877                 if (IsPrintable(c))
1878                         ost << c;
1879                 else if (c == META_INSET) {
1880                         getInset(i)->ascii(buffer, ost);
1881                 }
1882         }
1883
1884         return ost.str().c_str();
1885 }
1886
1887
1888 void Paragraph::setInsetOwner(Inset * i)
1889 {
1890         pimpl_->inset_owner = i;
1891         for (InsetList::const_iterator cit = insetlist.begin();
1892              cit != insetlist.end(); ++cit) {
1893                 if (cit->inset)
1894                         cit->inset->setOwner(i);
1895         }
1896 }
1897
1898
1899 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1900 {
1901         // then the insets
1902         for (InsetList::const_iterator cit = insetlist.begin();
1903              cit != insetlist.end(); ++cit) {
1904                 if (cit->inset) {
1905                         if (cit->inset->isTextInset()) {
1906                                 static_cast<UpdatableInset *>
1907                                         (cit->inset)->deleteLyXText(bv, true);
1908                         }
1909                 }
1910         }
1911 }
1912
1913
1914 void Paragraph::resizeInsetsLyXText(BufferView * bv)
1915 {
1916         // then the insets
1917         for (InsetList::const_iterator cit = insetlist.begin();
1918              cit != insetlist.end(); ++cit) {
1919                 if (cit->inset) {
1920                         if (cit->inset->isTextInset()) {
1921                                 static_cast<UpdatableInset *>
1922                                         (cit->inset)->resizeLyXText(bv, true);
1923                         }
1924                 }
1925         }
1926 }
1927
1928
1929 void Paragraph::setContentsFromPar(Paragraph * par)
1930 {
1931         pimpl_->setContentsFromPar(par);
1932 }
1933
1934
1935 Paragraph::size_type Paragraph::size() const
1936 {
1937         return pimpl_->size();
1938 }
1939
1940
1941 Paragraph::value_type
1942 Paragraph::getChar(Paragraph::size_type pos) const
1943 {
1944         return pimpl_->getChar(pos);
1945 }
1946
1947
1948 int Paragraph::id() const
1949 {
1950         return pimpl_->id_;
1951 }
1952
1953
1954 void  Paragraph::id(int id_arg)
1955 {
1956         pimpl_->id_ = id_arg;
1957 }
1958
1959
1960 LyXTextClass::LayoutList::size_type Paragraph::getLayout() const
1961 {
1962         return layout;
1963 }
1964
1965
1966 bool Paragraph::isFirstInSequence() const
1967 {
1968         Paragraph const * dhook = depthHook(getDepth());
1969         return (dhook == this
1970                 || dhook->getLayout() != getLayout()
1971                 || dhook->getDepth() != getDepth());
1972 }
1973
1974
1975 Inset * Paragraph::inInset() const
1976 {
1977         return pimpl_->inset_owner;
1978 }
1979
1980
1981 void Paragraph::clearContents()
1982 {
1983         pimpl_->clear();
1984 }
1985
1986
1987 void Paragraph::setCounter(int i, int v)
1988 {
1989         pimpl_->counter_[i] = v;
1990 }
1991
1992
1993 int Paragraph::getCounter(int i) const
1994 {
1995         return pimpl_->counter_[i];
1996 }
1997
1998
1999 void Paragraph::incCounter(int i)
2000 {
2001         pimpl_->counter_[i]++;
2002 }
2003
2004
2005 void Paragraph::setChar(size_type pos, value_type c)
2006 {
2007         pimpl_->setChar(pos, c);
2008 }
2009
2010
2011 Paragraph::inset_iterator::inset_iterator(Paragraph::InsetList::iterator const & iter)
2012  : it(iter) 
2013 {}
2014
2015
2016 Paragraph::inset_iterator Paragraph::inset_iterator_begin()
2017 {
2018         return inset_iterator(insetlist.begin());
2019 }
2020
2021
2022 Paragraph::inset_iterator Paragraph::inset_iterator_end()
2023 {
2024         return inset_iterator(insetlist.end());
2025 }
2026
2027
2028 ParagraphParameters & Paragraph::params()
2029 {
2030         return pimpl_->params;
2031 }
2032
2033
2034 ParagraphParameters const & Paragraph::params() const
2035 {
2036         return pimpl_->params;
2037 }
2038
2039
2040 Paragraph * Paragraph::getParFromID(int id) const
2041 {
2042         return pimpl_->getParFromID(id);
2043 }