]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
adjust some comments
[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 #include "paragraph.h"
14 #include "paragraph_pimpl.h"
15
16 #include "buffer.h"
17 #include "bufferparams.h"
18 #include "BufferView.h"
19 #include "changes.h"
20 #include "encoding.h"
21 #include "debug.h"
22 #include "gettext.h"
23 #include "language.h"
24 #include "latexrunparams.h"
25 #include "layout.h"
26 #include "lyxrc.h"
27 #include "paragraph_funcs.h"
28 #include "ParameterStruct.h"
29 #include "texrow.h"
30
31 #include "Lsstream.h"
32
33 #include "insets/insetbibitem.h"
34 #include "insets/insetoptarg.h"
35 #include "insets/insetenv.h"
36
37 #include "support/filetools.h"
38 #include "support/lstrings.h"
39 #include "support/lyxmanip.h"
40 #include "support/FileInfo.h"
41 #include "support/LAssert.h"
42 #include "support/textutils.h"
43
44 #include <algorithm>
45 #include <fstream>
46 #include <csignal>
47 #include <ctime>
48
49 using namespace lyx::support;
50
51 using std::ostream;
52 using std::endl;
53 using std::fstream;
54 using std::ios;
55 using std::lower_bound;
56 using std::upper_bound;
57
58 using lyx::pos_type;
59
60
61 Paragraph::Paragraph()
62         : pimpl_(new Paragraph::Pimpl(this))
63 {
64         enumdepth = 0;
65         itemdepth = 0;
66         params().clear();
67 }
68
69
70 Paragraph::Paragraph(Paragraph const & lp)
71         : pimpl_(new Paragraph::Pimpl(*lp.pimpl_, this))
72 {
73         enumdepth = 0;
74         itemdepth = 0;
75         // this is because of the dummy layout of the paragraphs that
76         // follow footnotes
77         layout_ = lp.layout();
78
79         // copy everything behind the break-position to the new paragraph
80         insetlist = lp.insetlist;
81         InsetList::iterator it = insetlist.begin();
82         InsetList::iterator end = insetlist.end();
83         for (; it != end; ++it) {
84                 // currently we hold Inset*, not InsetBase*
85                 it->inset = static_cast<InsetOld*>(it->inset->clone().release());
86                 // tell the new inset who is the boss now
87                 it->inset->parOwner(this);
88         }
89 }
90
91
92 void Paragraph::operator=(Paragraph const & lp)
93 {
94         // needed as we will destroy the pimpl_ before copying it
95         if (&lp != this)
96                 return;
97         lyxerr << "Paragraph::operator=()\n";
98         delete pimpl_;
99         pimpl_ = new Pimpl(*lp.pimpl_, this);
100
101         enumdepth = lp.enumdepth;
102         itemdepth = lp.itemdepth;
103         // this is because of the dummy layout of the paragraphs that
104         // follow footnotes
105         layout_ = lp.layout();
106
107         // copy everything behind the break-position to the new paragraph
108         insetlist = lp.insetlist;
109         InsetList::iterator it = insetlist.begin();
110         InsetList::iterator end = insetlist.end();
111         for (; it != end; ++it) {
112                 it->inset = static_cast<InsetOld*>(it->inset->clone().release());
113                 // tell the new inset who is the boss now
114                 it->inset->parOwner(this);
115         }
116 }
117
118 // the destructor removes the new paragraph from the list
119 Paragraph::~Paragraph()
120 {
121         delete pimpl_;
122         //
123         //lyxerr << "Paragraph::paragraph_id = "
124         //       << Paragraph::paragraph_id << endl;
125 }
126
127
128 void Paragraph::write(Buffer const * buf, ostream & os,
129                           BufferParams const & bparams,
130                           depth_type & dth) const
131 {
132         // The beginning or end of a deeper (i.e. nested) area?
133         if (dth != params().depth()) {
134                 if (params().depth() > dth) {
135                         while (params().depth() > dth) {
136                                 os << "\n\\begin_deeper ";
137                                 ++dth;
138                         }
139                 } else {
140                         while (params().depth() < dth) {
141                                 os << "\n\\end_deeper ";
142                                 --dth;
143                         }
144                 }
145         }
146
147         // First write the layout
148         os << "\n\\begin_layout " << layout()->name() << '\n';
149
150         params().write(os);
151
152         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
153
154         Change running_change = Change(Change::UNCHANGED);
155         lyx::time_type const curtime(lyx::current_time());
156
157         int column = 0;
158         for (pos_type i = 0; i < size(); ++i) {
159                 if (!i) {
160                         os << '\n';
161                         column = 0;
162                 }
163
164                 Change change = pimpl_->lookupChangeFull(i);
165                 Changes::lyxMarkChange(os, column, curtime, running_change, change);
166                 running_change = change;
167
168                 // Write font changes
169                 LyXFont font2 = getFontSettings(bparams, i);
170                 if (font2 != font1) {
171                         font2.lyxWriteChanges(font1, os);
172                         column = 0;
173                         font1 = font2;
174                 }
175
176                 value_type const c = getChar(i);
177                 switch (c) {
178                 case META_INSET:
179                 {
180                         InsetOld const * inset = getInset(i);
181                         if (inset)
182                                 if (inset->directWrite()) {
183                                         // international char, let it write
184                                         // code directly so it's shorter in
185                                         // the file
186                                         inset->write(buf, os);
187                                 } else {
188                                         os << "\n\\begin_inset ";
189                                         inset->write(buf, os);
190                                         os << "\n\\end_inset \n\n";
191                                         column = 0;
192                                 }
193                 }
194                 break;
195                 case '\\':
196                         os << "\n\\backslash \n";
197                         column = 0;
198                         break;
199                 case '.':
200                         if (i + 1 < size() && getChar(i + 1) == ' ') {
201                                 os << ".\n";
202                                 column = 0;
203                         } else
204                                 os << '.';
205                         break;
206                 default:
207                         if ((column > 70 && c == ' ')
208                             || column > 79) {
209                                 os << '\n';
210                                 column = 0;
211                         }
212                         // this check is to amend a bug. LyX sometimes
213                         // inserts '\0' this could cause problems.
214                         if (c != '\0')
215                                 os << c;
216                         else
217                                 lyxerr << "ERROR (Paragraph::writeFile):"
218                                         " NULL char in structure." << endl;
219                         ++column;
220                         break;
221                 }
222         }
223
224         // to make reading work properly
225         if (!size()) {
226                 running_change = pimpl_->lookupChange(0);
227                 Changes::lyxMarkChange(os, column, curtime,
228                         Change(Change::UNCHANGED), running_change);
229         }
230         Changes::lyxMarkChange(os, column, curtime,
231                 running_change, Change(Change::UNCHANGED));
232
233         os << "\n\\end_layout\n";
234 }
235
236
237 void Paragraph::validate(LaTeXFeatures & features) const
238 {
239         pimpl_->validate(features, *layout());
240 }
241
242
243 void Paragraph::eraseIntern(lyx::pos_type pos)
244 {
245         pimpl_->eraseIntern(pos);
246 }
247
248
249 bool Paragraph::erase(pos_type pos)
250 {
251         return pimpl_->erase(pos);
252 }
253
254
255 int Paragraph::erase(pos_type start, pos_type end)
256 {
257         return pimpl_->erase(start, end);
258 }
259
260
261 bool Paragraph::checkInsertChar(LyXFont & font)
262 {
263         if (pimpl_->inset_owner)
264                 return pimpl_->inset_owner->checkInsertChar(font);
265         return true;
266 }
267
268
269 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
270 {
271         insertChar(pos, c, LyXFont(LyXFont::ALL_INHERIT));
272 }
273
274
275 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
276                            LyXFont const & font, Change change)
277 {
278         pimpl_->insertChar(pos, c, font, change);
279 }
280
281
282 void Paragraph::insertInset(pos_type pos, InsetOld * inset)
283 {
284         insertInset(pos, inset, LyXFont(LyXFont::ALL_INHERIT));
285 }
286
287
288 void Paragraph::insertInset(pos_type pos, InsetOld * inset, LyXFont const & font, Change change)
289 {
290         pimpl_->insertInset(pos, inset, font, change);
291 }
292
293
294 bool Paragraph::insetAllowed(InsetOld::Code code)
295 {
296         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
297         if (pimpl_->inset_owner)
298                 return pimpl_->inset_owner->insetAllowed(code);
299         return true;
300 }
301
302
303 InsetOld * Paragraph::getInset(pos_type pos)
304 {
305         Assert(pos < size());
306         return insetlist.get(pos);
307 }
308
309
310 InsetOld const * Paragraph::getInset(pos_type pos) const
311 {
312         Assert(pos < size());
313         return insetlist.get(pos);
314 }
315
316
317 // Gets uninstantiated font setting at position.
318 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
319                                          pos_type pos) const
320 {
321         Assert(pos <= size());
322
323         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
324         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
325         for (; cit != end; ++cit)
326                 if (cit->pos() >= pos)
327                         break;
328
329         if (cit != end)
330                 return cit->font();
331
332         if (pos == size() && !empty())
333                 return getFontSettings(bparams, pos - 1);
334
335         return LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
336 }
337
338
339 lyx::pos_type Paragraph::getEndPosOfFontSpan(lyx::pos_type pos) const
340 {
341         Assert(pos <= size());
342
343         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
344         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
345         for (; cit != end; ++cit)
346                 if (cit->pos() >= pos)
347                         return cit->pos();
348
349         // This should not happen, but if so, we take no chances.
350         lyxerr << "Pararaph::getEndPosOfFontSpan: This should not happen!\n";
351         return pos;
352 }
353
354
355 // Gets uninstantiated font setting at position 0
356 LyXFont const Paragraph::getFirstFontSettings() const
357 {
358         if (!empty() && !pimpl_->fontlist.empty())
359                 return pimpl_->fontlist[0].font();
360
361         return LyXFont(LyXFont::ALL_INHERIT);
362 }
363
364
365 // Gets the fully instantiated font at a given position in a paragraph
366 // This is basically the same function as LyXText::GetFont() in text2.C.
367 // The difference is that this one is used for generating the LaTeX file,
368 // and thus cosmetic "improvements" are disallowed: This has to deliver
369 // the true picture of the buffer. (Asger)
370 LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
371                                  LyXFont const & outerfont) const
372 {
373         Assert(pos >= 0);
374
375         LyXLayout_ptr const & lout = layout();
376
377         pos_type const body_pos = beginningOfBody();
378
379         LyXFont layoutfont;
380         if (pos < body_pos)
381                 layoutfont = lout->labelfont;
382         else
383                 layoutfont = lout->font;
384
385         LyXFont tmpfont = getFontSettings(bparams, pos);
386         tmpfont.realize(layoutfont);
387         tmpfont.realize(outerfont);
388         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
389
390         return tmpfont;
391 }
392
393
394 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams,
395                                       LyXFont const & outerfont) const
396 {
397         LyXFont tmpfont = layout()->labelfont;
398         tmpfont.setLanguage(getParLanguage(bparams));
399         tmpfont.realize(outerfont);
400         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
401         return tmpfont;
402 }
403
404
405 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams,
406                                        LyXFont const & outerfont) const
407 {
408         LyXFont tmpfont = layout()->font;
409         tmpfont.setLanguage(getParLanguage(bparams));
410         tmpfont.realize(outerfont);
411         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
412         return tmpfont;
413 }
414
415
416 /// Returns the height of the highest font in range
417 LyXFont::FONT_SIZE
418 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
419                               LyXFont::FONT_SIZE const def_size) const
420 {
421         if (pimpl_->fontlist.empty())
422                 return def_size;
423
424         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
425         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
426         for (; end_it != end; ++end_it) {
427                 if (end_it->pos() >= endpos)
428                         break;
429         }
430
431         if (end_it != end)
432                 ++end_it;
433
434         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
435         for (; cit != end; ++cit) {
436                 if (cit->pos() >= startpos)
437                         break;
438         }
439
440         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
441         for (; cit != end_it; ++cit) {
442                 LyXFont::FONT_SIZE size = cit->font().size();
443                 if (size == LyXFont::INHERIT_SIZE)
444                         size = def_size;
445                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
446                         maxsize = size;
447         }
448         return maxsize;
449 }
450
451
452 Paragraph::value_type
453 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
454 {
455         value_type c = getChar(pos);
456         if (!lyxrc.rtl_support)
457                 return c;
458
459         value_type uc = c;
460         switch (c) {
461         case '(':
462                 uc = ')';
463                 break;
464         case ')':
465                 uc = '(';
466                 break;
467         case '[':
468                 uc = ']';
469                 break;
470         case ']':
471                 uc = '[';
472                 break;
473         case '{':
474                 uc = '}';
475                 break;
476         case '}':
477                 uc = '{';
478                 break;
479         case '<':
480                 uc = '>';
481                 break;
482         case '>':
483                 uc = '<';
484                 break;
485         }
486         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
487                 return uc;
488         else
489                 return c;
490 }
491
492
493 void Paragraph::setFont(pos_type pos, LyXFont const & font)
494 {
495         Assert(pos <= size());
496
497         // First, reduce font against layout/label font
498         // Update: The SetCharFont() routine in text2.C already
499         // reduces font, so we don't need to do that here. (Asger)
500         // No need to simplify this because it will disappear
501         // in a new kernel. (Asger)
502         // Next search font table
503
504         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
505         Pimpl::FontList::iterator it = beg;
506         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
507         for (; it != endit; ++it) {
508                 if (it->pos() >= pos)
509                         break;
510         }
511         unsigned int i = std::distance(beg, it);
512         bool notfound = (it == endit);
513
514         if (!notfound && pimpl_->fontlist[i].font() == font)
515                 return;
516
517         bool begin = pos == 0 || notfound ||
518                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
519         // Is position pos is a beginning of a font block?
520         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
521         // Is position pos is the end of a font block?
522         if (begin && end) { // A single char block
523                 if (i + 1 < pimpl_->fontlist.size() &&
524                     pimpl_->fontlist[i + 1].font() == font) {
525                         // Merge the singleton block with the next block
526                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
527                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
528                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
529                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
530                         // Merge the singleton block with the previous block
531                         pimpl_->fontlist[i - 1].pos(pos);
532                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
533                 } else
534                         pimpl_->fontlist[i].font(font);
535         } else if (begin) {
536                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
537                         pimpl_->fontlist[i - 1].pos(pos);
538                 else
539                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
540                                         Pimpl::FontTable(pos, font));
541         } else if (end) {
542                 pimpl_->fontlist[i].pos(pos - 1);
543                 if (!(i + 1 < pimpl_->fontlist.size() &&
544                       pimpl_->fontlist[i + 1].font() == font))
545                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
546                                         Pimpl::FontTable(pos, font));
547         } else { // The general case. The block is splitted into 3 blocks
548                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
549                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
550                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
551                                 Pimpl::FontTable(pos, font));
552         }
553 }
554
555
556 void Paragraph::makeSameLayout(Paragraph const & par)
557 {
558         layout(par.layout());
559         // move to pimpl?
560         params() = par.params();
561 }
562
563
564 int Paragraph::stripLeadingSpaces()
565 {
566         if (isFreeSpacing())
567                 return 0;
568
569         int i = 0;
570         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
571                 pimpl_->eraseIntern(0);
572                 ++i;
573         }
574
575         return i;
576 }
577
578
579 bool Paragraph::hasSameLayout(Paragraph const & par) const
580 {
581         return
582                 par.layout() == layout() &&
583                 params().sameLayout(par.params());
584 }
585
586
587 Paragraph::depth_type Paragraph::getDepth() const
588 {
589         return params().depth();
590 }
591
592
593 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
594 {
595         if (layout()->isEnvironment())
596                 return params().depth() + 1;
597         else
598                 return params().depth();
599 }
600
601
602 char Paragraph::getAlign() const
603 {
604         return params().align();
605 }
606
607
608 string const & Paragraph::getLabelstring() const
609 {
610         return params().labelString();
611 }
612
613
614 // the next two functions are for the manual labels
615 string const Paragraph::getLabelWidthString() const
616 {
617         if (!params().labelWidthString().empty())
618                 return params().labelWidthString();
619         else
620                 return _("Senseless with this layout!");
621 }
622
623
624 void Paragraph::setLabelWidthString(string const & s)
625 {
626         params().labelWidthString(s);
627 }
628
629
630 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
631 {
632         layout(new_layout);
633         params().labelWidthString(string());
634         params().align(LYX_ALIGN_LAYOUT);
635         params().spaceTop(VSpace(VSpace::NONE));
636         params().spaceBottom(VSpace(VSpace::NONE));
637         params().spacing(Spacing(Spacing::Default));
638 }
639
640
641 int Paragraph::beginningOfBody() const
642 {
643         if (layout()->labeltype != LABEL_MANUAL)
644                 return 0;
645
646         // Unroll the first two cycles of the loop
647         // and remember the previous character to
648         // remove unnecessary GetChar() calls
649         pos_type i = 0;
650         if (i < size() && !isNewline(i)) {
651                 ++i;
652                 char previous_char = 0;
653                 char temp = 0;
654                 if (i < size()) {
655                         previous_char = getChar(i);
656                         if (!isNewline(i)) {
657                                 ++i;
658                                 while (i < size() && previous_char != ' ') {
659                                         temp = getChar(i);
660                                         if (isNewline(i))
661                                                 break;
662
663                                         ++i;
664                                         previous_char = temp;
665                                 }
666                         }
667                 }
668         }
669
670         return i;
671 }
672
673
674 // returns -1 if inset not found
675 int Paragraph::getPositionOfInset(InsetOld const * inset) const
676 {
677         // Find the entry.
678         InsetList::const_iterator it = insetlist.begin();
679         InsetList::const_iterator end = insetlist.end();
680         for (; it != end; ++it)
681                 if (it->inset == inset)
682                         return it->pos;
683         return -1;
684 }
685
686
687 InsetBibitem * Paragraph::bibitem() const
688 {
689         InsetList::const_iterator it = insetlist.begin();
690         if (it != insetlist.end() && it->inset->lyxCode() == InsetOld::BIBTEX_CODE)
691                 return static_cast<InsetBibitem *>(it->inset);
692         return 0;
693 }
694
695
696
697 // This could go to ParagraphParameters if we want to
698 int Paragraph::startTeXParParams(BufferParams const & bparams,
699                                  ostream & os, bool moving_arg) const
700 {
701         int column = 0;
702
703         if (params().noindent()) {
704                 os << "\\noindent ";
705                 column += 10;
706         }
707
708         switch (params().align()) {
709         case LYX_ALIGN_NONE:
710         case LYX_ALIGN_BLOCK:
711         case LYX_ALIGN_LAYOUT:
712         case LYX_ALIGN_SPECIAL:
713                 break;
714         case LYX_ALIGN_LEFT:
715         case LYX_ALIGN_RIGHT:
716         case LYX_ALIGN_CENTER:
717                 if (moving_arg) {
718                         os << "\\protect";
719                         column = 8;
720                 }
721                 break;
722         }
723
724         switch (params().align()) {
725         case LYX_ALIGN_NONE:
726         case LYX_ALIGN_BLOCK:
727         case LYX_ALIGN_LAYOUT:
728         case LYX_ALIGN_SPECIAL:
729                 break;
730         case LYX_ALIGN_LEFT:
731                 if (getParLanguage(bparams)->babel() != "hebrew") {
732                         os << "\\begin{flushleft}";
733                         column += 17;
734                 } else {
735                         os << "\\begin{flushright}";
736                         column += 18;
737                 }
738                 break;
739         case LYX_ALIGN_RIGHT:
740                 if (getParLanguage(bparams)->babel() != "hebrew") {
741                         os << "\\begin{flushright}";
742                         column += 18;
743                 } else {
744                         os << "\\begin{flushleft}";
745                         column += 17;
746                 }
747                 break;
748         case LYX_ALIGN_CENTER:
749                 os << "\\begin{center}";
750                 column += 14;
751                 break;
752         }
753
754         return column;
755 }
756
757
758 // This could go to ParagraphParameters if we want to
759 int Paragraph::endTeXParParams(BufferParams const & bparams,
760                                ostream & os, bool moving_arg) const
761 {
762         int column = 0;
763
764         switch (params().align()) {
765         case LYX_ALIGN_NONE:
766         case LYX_ALIGN_BLOCK:
767         case LYX_ALIGN_LAYOUT:
768         case LYX_ALIGN_SPECIAL:
769                 break;
770         case LYX_ALIGN_LEFT:
771         case LYX_ALIGN_RIGHT:
772         case LYX_ALIGN_CENTER:
773                 if (moving_arg) {
774                         os << "\\protect";
775                         column = 8;
776                 }
777                 break;
778         }
779
780         switch (params().align()) {
781         case LYX_ALIGN_NONE:
782         case LYX_ALIGN_BLOCK:
783         case LYX_ALIGN_LAYOUT:
784         case LYX_ALIGN_SPECIAL:
785                 break;
786         case LYX_ALIGN_LEFT:
787                 if (getParLanguage(bparams)->babel() != "hebrew") {
788                         os << "\\end{flushleft}";
789                         column = 15;
790                 } else {
791                         os << "\\end{flushright}";
792                         column = 16;
793                 }
794                 break;
795         case LYX_ALIGN_RIGHT:
796                 if (getParLanguage(bparams)->babel() != "hebrew") {
797                         os << "\\end{flushright}";
798                         column+= 16;
799                 } else {
800                         os << "\\end{flushleft}";
801                         column = 15;
802                 }
803                 break;
804         case LYX_ALIGN_CENTER:
805                 os << "\\end{center}";
806                 column = 12;
807                 break;
808         }
809         return column;
810 }
811
812
813 // This one spits out the text of the paragraph
814 bool Paragraph::simpleTeXOnePar(Buffer const * buf,
815                                 BufferParams const & bparams,
816                                 LyXFont const & outerfont,
817                                 ostream & os, TexRow & texrow,
818                                 LatexRunParams const & runparams)
819 {
820         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
821
822         bool return_value = false;
823
824         LyXLayout_ptr style;
825
826         // well we have to check if we are in an inset with unlimited
827         // lenght (all in one row) if that is true then we don't allow
828         // any special options in the paragraph and also we don't allow
829         // any environment other then "Standard" to be valid!
830         bool asdefault =
831                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
832
833         if (asdefault) {
834                 style = bparams.getLyXTextClass().defaultLayout();
835         } else {
836                 style = layout();
837         }
838
839         LyXFont basefont;
840
841         // Maybe we have to create a optional argument.
842         pos_type body_pos;
843
844         // FIXME: can we actually skip this check and just call
845         // beginningOfBody() ??
846         if (style->labeltype != LABEL_MANUAL) {
847                 body_pos = 0;
848         } else {
849                 body_pos = beginningOfBody();
850         }
851
852         unsigned int column = 0;
853
854         if (body_pos > 0) {
855                 os << '[';
856                 ++column;
857                 basefont = getLabelFont(bparams, outerfont);
858         } else {
859                 basefont = getLayoutFont(bparams, outerfont);
860         }
861
862         bool moving_arg = runparams.moving_arg;
863         moving_arg |= style->needprotect;
864
865         // Which font is currently active?
866         LyXFont running_font(basefont);
867         // Do we have an open font change?
868         bool open_font = false;
869
870         Change::Type running_change = Change::UNCHANGED;
871
872         texrow.start(id(), 0);
873
874         // if the paragraph is empty, the loop will not be entered at all
875         if (empty()) {
876                 if (style->isCommand()) {
877                         os << '{';
878                         ++column;
879                 }
880                 if (!asdefault)
881                         column += startTeXParParams(bparams, os, moving_arg);
882
883         }
884
885         for (pos_type i = 0; i < size(); ++i) {
886                 ++column;
887                 // First char in paragraph or after label?
888                 if (i == body_pos) {
889                         if (body_pos > 0) {
890                                 if (open_font) {
891                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
892                                         open_font = false;
893                                 }
894                                 basefont = getLayoutFont(bparams, outerfont);
895                                 running_font = basefont;
896                                 os << ']';
897                                 ++column;
898                         }
899                         if (style->isCommand()) {
900                                 os << '{';
901                                 ++column;
902                         }
903
904                         if (!asdefault)
905                                 column += startTeXParParams(bparams, os,
906                                                             moving_arg);
907                 }
908
909                 value_type c = getChar(i);
910
911                 // Fully instantiated font
912                 LyXFont font = getFont(bparams, i, outerfont);
913
914                 LyXFont const last_font = running_font;
915
916                 // Spaces at end of font change are simulated to be
917                 // outside font change, i.e. we write "\textXX{text} "
918                 // rather than "\textXX{text }". (Asger)
919                 if (open_font && c == ' ' && i <= size() - 2) {
920                         LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
921                         if (next_font != running_font
922                             && next_font != font) {
923                                 font = next_font;
924                         }
925                 }
926
927                 // We end font definition before blanks
928                 if (open_font &&
929                     (font != running_font ||
930                      font.language() != running_font.language()))
931                 {
932                         column += running_font.latexWriteEndChanges(os,
933                                                                     basefont,
934                                                                     (i == body_pos-1) ? basefont : font);
935                         running_font = basefont;
936                         open_font = false;
937                 }
938
939                 // Blanks are printed before start of fontswitch
940                 if (c == ' ') {
941                         // Do not print the separation of the optional argument
942                         if (i != body_pos - 1) {
943                                 pimpl_->simpleTeXBlanks(os, texrow, i,
944                                                        column, font, *style);
945                         }
946                 }
947
948                 // Do we need to change font?
949                 if ((font != running_font ||
950                      font.language() != running_font.language()) &&
951                         i != body_pos - 1)
952                 {
953                         column += font.latexWriteStartChanges(os, basefont,
954                                                               last_font);
955                         running_font = font;
956                         open_font = true;
957                 }
958
959                 Change::Type change = pimpl_->lookupChange(i);
960
961                 column += Changes::latexMarkChange(os, running_change, change);
962                 running_change = change;
963
964                 LatexRunParams rp = runparams;
965                 rp.moving_arg = moving_arg;
966                 rp.free_spacing = style->free_spacing;
967                 pimpl_->simpleTeXSpecialChars(buf, bparams,
968                                               os, texrow, runparams,
969                                               font, running_font,
970                                               basefont, outerfont, open_font,
971                                               running_change,
972                                               *style, i, column, c);
973         }
974
975         column += Changes::latexMarkChange(os,
976                         running_change, Change::UNCHANGED);
977
978         // If we have an open font definition, we have to close it
979         if (open_font) {
980 #ifdef FIXED_LANGUAGE_END_DETECTION
981                 if (next_) {
982                         running_font
983                                 .latexWriteEndChanges(os, basefont,
984                                                       next_->getFont(bparams,
985                                                       0, outerfont));
986                 } else {
987                         running_font.latexWriteEndChanges(os, basefont,
988                                                           basefont);
989                 }
990 #else
991 #ifdef WITH_WARNINGS
992 //#warning For now we ALWAYS have to close the foreign font settings if they are
993 //#warning there as we start another \selectlanguage with the next paragraph if
994 //#warning we are in need of this. This should be fixed sometime (Jug)
995 #endif
996                 running_font.latexWriteEndChanges(os, basefont,  basefont);
997 #endif
998         }
999
1000         // Needed if there is an optional argument but no contents.
1001         if (body_pos > 0 && body_pos == size()) {
1002                 os << "]~";
1003                 return_value = false;
1004         }
1005
1006         if (!asdefault) {
1007                 column += endTeXParParams(bparams, os, moving_arg);
1008         }
1009
1010         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1011         return return_value;
1012 }
1013
1014
1015
1016
1017 bool Paragraph::isHfill(pos_type pos) const
1018 {
1019         return IsInsetChar(getChar(pos))
1020                && getInset(pos)->lyxCode() == InsetOld::HFILL_CODE;
1021 }
1022
1023
1024 bool Paragraph::isInset(pos_type pos) const
1025 {
1026         return IsInsetChar(getChar(pos));
1027 }
1028
1029
1030 bool Paragraph::isNewline(pos_type pos) const
1031 {
1032         return IsInsetChar(getChar(pos))
1033                && getInset(pos)->lyxCode() == InsetOld::NEWLINE_CODE;
1034 }
1035
1036
1037 bool Paragraph::isSeparator(pos_type pos) const
1038 {
1039         return IsSeparatorChar(getChar(pos));
1040 }
1041
1042
1043 bool Paragraph::isLineSeparator(pos_type pos) const
1044 {
1045         value_type const c = getChar(pos);
1046         return IsLineSeparatorChar(c)
1047                 || (IsInsetChar(c) && getInset(pos) &&
1048                 getInset(pos)->isLineSeparator());
1049 }
1050
1051
1052 bool Paragraph::isKomma(pos_type pos) const
1053 {
1054         return IsKommaChar(getChar(pos));
1055 }
1056
1057
1058 /// Used by the spellchecker
1059 bool Paragraph::isLetter(pos_type pos) const
1060 {
1061         value_type const c = getChar(pos);
1062         if (IsLetterChar(c))
1063                 return true;
1064         if (isInset(pos))
1065                 return getInset(pos)->isLetter();
1066         // We want to pass the ' and escape chars to ispell
1067         string const extra = lyxrc.isp_esc_chars + '\'';
1068         return contains(extra, c);
1069 }
1070
1071
1072 bool Paragraph::isWord(pos_type pos) const
1073 {
1074         return IsWordChar(getChar(pos)) ;
1075 }
1076
1077
1078 Language const *
1079 Paragraph::getParLanguage(BufferParams const & bparams) const
1080 {
1081         if (!empty())
1082                 return getFirstFontSettings().language();
1083 #warning FIXME we should check the prev par as well (Lgb)
1084         return bparams.language;
1085 }
1086
1087
1088 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1089 {
1090         return lyxrc.rtl_support
1091                 && getParLanguage(bparams)->RightToLeft()
1092                 && !(inInset() && inInset()->owner() &&
1093                      inInset()->owner()->lyxCode() == InsetOld::ERT_CODE);
1094 }
1095
1096
1097 void Paragraph::changeLanguage(BufferParams const & bparams,
1098                                Language const * from, Language const * to)
1099 {
1100         for (pos_type i = 0; i < size(); ++i) {
1101                 LyXFont font = getFontSettings(bparams, i);
1102                 if (font.language() == from) {
1103                         font.setLanguage(to);
1104                         setFont(i, font);
1105                 }
1106         }
1107 }
1108
1109
1110 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1111 {
1112         Language const * doc_language = bparams.language;
1113         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1114         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1115
1116         for (; cit != end; ++cit)
1117                 if (cit->font().language() != ignore_language &&
1118                     cit->font().language() != latex_language &&
1119                     cit->font().language() != doc_language)
1120                         return true;
1121         return false;
1122 }
1123
1124
1125 // Convert the paragraph to a string.
1126 // Used for building the table of contents
1127 string const Paragraph::asString(Buffer const * buffer, bool label) const
1128 {
1129 #if 0
1130         string s;
1131         if (label && !params().labelString().empty())
1132                 s += params().labelString() + ' ';
1133
1134         for (pos_type i = 0; i < size(); ++i) {
1135                 value_type c = getChar(i);
1136                 if (IsPrintable(c))
1137                         s += c;
1138                 else if (c == META_INSET &&
1139                          getInset(i)->lyxCode() == InsetOld::MATH_CODE) {
1140                         ostringstream ost;
1141                         getInset(i)->ascii(buffer, ost);
1142                         s += subst(STRCONV(ost.str()),'\n',' ');
1143                 }
1144         }
1145
1146         return s;
1147 #else
1148         // This should really be done by the caller and not here.
1149         string ret(asString(buffer, 0, size(), label));
1150         return subst(ret, '\n', ' ');
1151 #endif
1152 }
1153
1154
1155 string const Paragraph::asString(Buffer const * buffer,
1156                                  pos_type beg, pos_type end, bool label) const
1157 {
1158         ostringstream os;
1159
1160         if (beg == 0 && label && !params().labelString().empty())
1161                 os << params().labelString() << ' ';
1162
1163         for (pos_type i = beg; i < end; ++i) {
1164                 value_type const c = getUChar(buffer->params, i);
1165                 if (IsPrintable(c))
1166                         os << c;
1167                 else if (c == META_INSET)
1168                         getInset(i)->ascii(buffer, os);
1169         }
1170
1171         return STRCONV(os.str());
1172 }
1173
1174
1175 void Paragraph::setInsetOwner(UpdatableInset * inset)
1176 {
1177         pimpl_->inset_owner = inset;
1178         InsetList::iterator it = insetlist.begin();
1179         InsetList::iterator end = insetlist.end();
1180         for (; it != end; ++it)
1181                 if (it->inset)
1182                         it->inset->setOwner(inset);
1183 }
1184
1185
1186 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1187 {
1188         insetlist.deleteInsetsLyXText(bv);
1189 }
1190
1191
1192 void Paragraph::resizeInsetsLyXText(BufferView * bv)
1193 {
1194         insetlist.resizeInsetsLyXText(bv);
1195 }
1196
1197
1198 void Paragraph::setContentsFromPar(Paragraph const & par)
1199 {
1200         pimpl_->setContentsFromPar(par);
1201 }
1202
1203
1204 void Paragraph::trackChanges(Change::Type type)
1205 {
1206         pimpl_->trackChanges(type);
1207 }
1208
1209
1210 void Paragraph::untrackChanges()
1211 {
1212         pimpl_->untrackChanges();
1213 }
1214
1215
1216 void Paragraph::cleanChanges()
1217 {
1218         pimpl_->cleanChanges();
1219 }
1220
1221
1222 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1223 {
1224         Assert(!size() || pos < size());
1225         return pimpl_->lookupChange(pos);
1226 }
1227
1228
1229 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1230 {
1231         Assert(!size() || pos < size());
1232         return pimpl_->lookupChangeFull(pos);
1233 }
1234
1235
1236 bool Paragraph::isChanged(pos_type start, pos_type end) const
1237 {
1238         return pimpl_->isChanged(start, end);
1239 }
1240
1241
1242 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1243 {
1244         return pimpl_->isChangeEdited(start, end);
1245 }
1246
1247
1248 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1249 {
1250         pimpl_->setChange(pos, type);
1251
1252 }
1253
1254
1255 void Paragraph::markErased()
1256 {
1257         pimpl_->markErased();
1258 }
1259
1260
1261 void Paragraph::acceptChange(pos_type start, pos_type end)
1262 {
1263         return pimpl_->acceptChange(start, end);
1264 }
1265
1266
1267 void Paragraph::rejectChange(pos_type start, pos_type end)
1268 {
1269         return pimpl_->rejectChange(start, end);
1270 }
1271
1272
1273 lyx::pos_type Paragraph::size() const
1274 {
1275         return pimpl_->size();
1276 }
1277
1278
1279 bool Paragraph::empty() const
1280 {
1281         return pimpl_->empty();
1282 }
1283
1284
1285 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1286 {
1287         return pimpl_->getChar(pos);
1288 }
1289
1290
1291 int Paragraph::id() const
1292 {
1293         return pimpl_->id_;
1294 }
1295
1296
1297 void Paragraph::id(int i)
1298 {
1299         pimpl_->id_ = i;
1300 }
1301
1302
1303 LyXLayout_ptr const & Paragraph::layout() const
1304 {
1305 /*
1306         InsetOld * inset = inInset();
1307         if (inset && inset->lyxCode() == InsetOld::ENVIRONMENT_CODE)
1308                 return static_cast<InsetEnvironment*>(inset)->layout();
1309 */
1310         return layout_;
1311 }
1312
1313
1314 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1315 {
1316         layout_ = new_layout;
1317 }
1318
1319
1320 UpdatableInset * Paragraph::inInset() const
1321 {
1322         return pimpl_->inset_owner;
1323 }
1324
1325
1326 void Paragraph::clearContents()
1327 {
1328         pimpl_->clear();
1329 }
1330
1331 void Paragraph::setChar(pos_type pos, value_type c)
1332 {
1333         pimpl_->setChar(pos, c);
1334 }
1335
1336
1337 ParagraphParameters & Paragraph::params()
1338 {
1339         return pimpl_->params;
1340 }
1341
1342
1343 ParagraphParameters const & Paragraph::params() const
1344 {
1345         return pimpl_->params;
1346 }
1347
1348
1349 bool Paragraph::isFreeSpacing() const
1350 {
1351         if (layout()->free_spacing)
1352                 return true;
1353
1354         // for now we just need this, later should we need this in some
1355         // other way we can always add a function to InsetOld::() too.
1356         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1357                 return pimpl_->inset_owner->owner()->lyxCode() == InsetOld::ERT_CODE;
1358         return false;
1359 }
1360
1361
1362 bool Paragraph::allowEmpty() const
1363 {
1364         if (layout()->keepempty)
1365                 return true;
1366         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1367                 return pimpl_->inset_owner->owner()->lyxCode() == InsetOld::ERT_CODE;
1368         return false;
1369 }