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