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