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