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