]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
place one paragraph parameter per line. (file format = 239)
[lyx.git] / src / paragraph.C
1 /**
2  * \file paragraph.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Dekel Tsur
13  * \author Jürgen Vigna
14  *
15  * Full author contact details are available in file CREDITS.
16  */
17
18 #include <config.h>
19
20 #include "paragraph.h"
21 #include "paragraph_pimpl.h"
22
23 #include "buffer.h"
24 #include "bufferparams.h"
25 #include "counters.h"
26 #include "encoding.h"
27 #include "debug.h"
28 #include "gettext.h"
29 #include "language.h"
30 #include "lyxfont.h"
31 #include "lyxrc.h"
32 #include "lyxrow.h"
33 #include "outputparams.h"
34 #include "paragraph_funcs.h"
35 #include "ParagraphList_fwd.h"
36 #include "sgml.h"
37 #include "texrow.h"
38 #include "vspace.h"
39
40 #include "insets/insetbibitem.h"
41 #include "insets/insetoptarg.h"
42
43 #include "support/lstrings.h"
44 #include "support/textutils.h"
45 #include "support/convert.h"
46
47 #include <boost/tuple/tuple.hpp>
48 #include <boost/bind.hpp>
49
50 #include <list>
51 #include <stack>
52 #include <sstream>
53
54 using lyx::pos_type;
55
56 using lyx::support::subst;
57
58 using std::distance;
59 using std::endl;
60 using std::list;
61 using std::stack;
62 using std::string;
63 using std::ostream;
64 using std::ostringstream;
65
66
67 ParagraphList::ParagraphList()
68 {}
69
70
71 Paragraph::Paragraph()
72         : begin_of_body_(0), pimpl_(new Paragraph::Pimpl(this))
73 {
74         itemdepth = 0;
75         params().clear();
76 }
77
78
79 Paragraph::Paragraph(Paragraph const & par)
80         :       itemdepth(par.itemdepth), insetlist(par.insetlist),
81                 dim_(par.dim_),
82                 rows_(par.rows_), layout_(par.layout_),
83                 text_(par.text_), begin_of_body_(par.begin_of_body_),
84           pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
85 {
86         //lyxerr << "Paragraph::Paragraph(Paragraph const&)" << endl;
87         InsetList::iterator it = insetlist.begin();
88         InsetList::iterator end = insetlist.end();
89         for (; it != end; ++it)
90                 it->inset = it->inset->clone().release();
91 }
92
93
94 Paragraph & Paragraph::operator=(Paragraph const & par)
95 {
96         // needed as we will destroy the pimpl_ before copying it
97         if (&par != this) {
98                 itemdepth = par.itemdepth;
99
100                 insetlist = par.insetlist;
101                 InsetList::iterator it = insetlist.begin();
102                 InsetList::iterator end = insetlist.end();
103                 for (; it != end; ++it)
104                         it->inset = it->inset->clone().release();
105
106                 rows_ = par.rows_;
107                 dim_ = par.dim_;
108                 layout_ = par.layout();
109                 text_ = par.text_;
110                 begin_of_body_ = par.begin_of_body_;
111
112                 delete pimpl_;
113                 pimpl_ = new Pimpl(*par.pimpl_, this);
114         }
115         return *this;
116 }
117
118
119 Paragraph::~Paragraph()
120 {
121         delete pimpl_;
122         //
123         //lyxerr << "Paragraph::paragraph_id = "
124         //       << Paragraph::paragraph_id << endl;
125 }
126
127
128 void Paragraph::write(Buffer const & buf, ostream & os,
129                           BufferParams const & bparams,
130                           depth_type & dth) const
131 {
132         // The beginning or end of a deeper (i.e. nested) area?
133         if (dth != params().depth()) {
134                 if (params().depth() > dth) {
135                         while (params().depth() > dth) {
136                                 os << "\n\\begin_deeper";
137                                 ++dth;
138                         }
139                 } else {
140                         while (params().depth() < dth) {
141                                 os << "\n\\end_deeper";
142                                 --dth;
143                         }
144                 }
145         }
146
147         // First write the layout
148         os << "\n\\begin_layout " << layout()->name() << '\n';
149
150         params().write(os);
151
152         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
153
154         Change running_change = Change(Change::UNCHANGED);
155         lyx::time_type const curtime(lyx::current_time());
156
157         int column = 0;
158         for (pos_type i = 0; i < size(); ++i) {
159
160                 Change change = pimpl_->lookupChangeFull(i);
161                 Changes::lyxMarkChange(os, column, curtime, running_change, change);
162                 running_change = change;
163
164                 // Write font changes
165                 LyXFont font2 = getFontSettings(bparams, i);
166                 if (font2 != font1) {
167                         font2.lyxWriteChanges(font1, os);
168                         column = 0;
169                         font1 = font2;
170                 }
171
172                 value_type const c = getChar(i);
173                 switch (c) {
174                 case META_INSET:
175                 {
176                         InsetBase const * inset = getInset(i);
177                         if (inset)
178                                 if (inset->directWrite()) {
179                                         // international char, let it write
180                                         // code directly so it's shorter in
181                                         // the file
182                                         inset->write(buf, os);
183                                 } else {
184                                         if (i)
185                                                 os << '\n';
186                                         os << "\\begin_inset ";
187                                         inset->write(buf, os);
188                                         os << "\n\\end_inset\n\n";
189                                         column = 0;
190                                 }
191                 }
192                 break;
193                 case '\\':
194                         os << "\n\\backslash\n";
195                         column = 0;
196                         break;
197                 case '.':
198                         if (i + 1 < size() && getChar(i + 1) == ' ') {
199                                 os << ".\n";
200                                 column = 0;
201                         } else
202                                 os << '.';
203                         break;
204                 default:
205                         if ((column > 70 && c == ' ')
206                             || column > 79) {
207                                 os << '\n';
208                                 column = 0;
209                         }
210                         // this check is to amend a bug. LyX sometimes
211                         // inserts '\0' this could cause problems.
212                         if (c != '\0')
213                                 os << c;
214                         else
215                                 lyxerr << "ERROR (Paragraph::writeFile):"
216                                         " NULL char in structure." << endl;
217                         ++column;
218                         break;
219                 }
220         }
221
222         // to make reading work properly
223         if (!size()) {
224                 running_change = pimpl_->lookupChange(0);
225                 Changes::lyxMarkChange(os, column, curtime,
226                         Change(Change::UNCHANGED), running_change);
227         }
228         Changes::lyxMarkChange(os, column, curtime,
229                 running_change, Change(Change::UNCHANGED));
230
231         os << "\n\\end_layout\n";
232 }
233
234
235 void Paragraph::validate(LaTeXFeatures & features) const
236 {
237         pimpl_->validate(features, *layout());
238 }
239
240
241 void Paragraph::eraseIntern(lyx::pos_type pos)
242 {
243         pimpl_->eraseIntern(pos);
244 }
245
246
247 bool Paragraph::erase(pos_type pos)
248 {
249         return pimpl_->erase(pos);
250 }
251
252
253 int Paragraph::erase(pos_type start, pos_type end)
254 {
255         return pimpl_->erase(start, end);
256 }
257
258
259 void Paragraph::insert(pos_type start, string const & str,
260                        LyXFont const & font)
261 {
262         int size = str.size();
263         for (int i = 0 ; i < size ; ++i)
264                 insertChar(start + i, str[i], font);
265 }
266
267
268 bool Paragraph::checkInsertChar(LyXFont &)
269 {
270         return true;
271 }
272
273
274 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
275                            Change change)
276 {
277         pimpl_->insertChar(pos, c, change);
278 }
279
280
281 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
282                            LyXFont const & font, Change change)
283 {
284         pimpl_->insertChar(pos, c, change);
285         setFont(pos, font);
286 }
287
288
289 void Paragraph::insertInset(pos_type pos, InsetBase * inset, Change change)
290 {
291         pimpl_->insertInset(pos, inset, change);
292 }
293
294
295 void Paragraph::insertInset(pos_type pos, InsetBase * inset,
296                             LyXFont const & font, Change change)
297 {
298         pimpl_->insertInset(pos, inset, change);
299         setFont(pos, font);
300 }
301
302
303 bool Paragraph::insetAllowed(InsetBase_code code)
304 {
305         return !pimpl_->inset_owner || pimpl_->inset_owner->insetAllowed(code);
306 }
307
308
309 InsetBase * Paragraph::getInset(pos_type pos)
310 {
311         BOOST_ASSERT(pos < size());
312         return insetlist.get(pos);
313 }
314
315
316 InsetBase const * Paragraph::getInset(pos_type pos) const
317 {
318         BOOST_ASSERT(pos < size());
319         return insetlist.get(pos);
320 }
321
322
323 // Gets uninstantiated font setting at position.
324 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
325                                          pos_type pos) const
326 {
327         if (pos > size()) {
328                 lyxerr << " pos: " << pos << " size: " << size() << endl;
329                 BOOST_ASSERT(pos <= size());
330         }
331
332         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
333         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
334         for (; cit != end; ++cit)
335                 if (cit->pos() >= pos)
336                         break;
337
338         if (cit != end)
339                 return cit->font();
340
341         if (pos == size() && !empty())
342                 return getFontSettings(bparams, pos - 1);
343
344         return LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
345 }
346
347
348 lyx::pos_type Paragraph::getEndPosOfFontSpan(lyx::pos_type pos) const
349 {
350         BOOST_ASSERT(pos <= size());
351
352         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
353         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
354         for (; cit != end; ++cit)
355                 if (cit->pos() >= pos)
356                         return cit->pos();
357
358         // This should not happen, but if so, we take no chances.
359         //lyxerr << "Paragraph::getEndPosOfFontSpan: This should not happen!"
360         //      << endl;
361         return pos;
362 }
363
364
365 // Gets uninstantiated font setting at position 0
366 LyXFont const Paragraph::getFirstFontSettings() const
367 {
368         if (!empty() && !pimpl_->fontlist.empty())
369                 return pimpl_->fontlist[0].font();
370
371         return LyXFont(LyXFont::ALL_INHERIT);
372 }
373
374
375 // Gets the fully instantiated font at a given position in a paragraph
376 // This is basically the same function as LyXText::GetFont() in text2.C.
377 // The difference is that this one is used for generating the LaTeX file,
378 // and thus cosmetic "improvements" are disallowed: This has to deliver
379 // the true picture of the buffer. (Asger)
380 LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
381                                  LyXFont const & outerfont) const
382 {
383         BOOST_ASSERT(pos >= 0);
384
385         LyXLayout_ptr const & lout = layout();
386
387         pos_type const body_pos = beginOfBody();
388
389         LyXFont layoutfont;
390         if (pos < body_pos)
391                 layoutfont = lout->labelfont;
392         else
393                 layoutfont = lout->font;
394
395         LyXFont font = getFontSettings(bparams, pos);
396         font.realize(layoutfont);
397         font.realize(outerfont);
398         font.realize(bparams.getLyXTextClass().defaultfont());
399
400         return font;
401 }
402
403
404 LyXFont const Paragraph::getLabelFont
405         (BufferParams const & bparams, LyXFont const & outerfont) const
406 {
407         LyXFont tmpfont = layout()->labelfont;
408         tmpfont.setLanguage(getParLanguage(bparams));
409         tmpfont.realize(outerfont);
410         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
411         return tmpfont;
412 }
413
414
415 LyXFont const Paragraph::getLayoutFont
416         (BufferParams const & bparams, LyXFont const & outerfont) const
417 {
418         LyXFont tmpfont = layout()->font;
419         tmpfont.setLanguage(getParLanguage(bparams));
420         tmpfont.realize(outerfont);
421         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
422         return tmpfont;
423 }
424
425
426 /// Returns the height of the highest font in range
427 LyXFont_size Paragraph::highestFontInRange
428         (pos_type startpos, pos_type endpos, LyXFont_size def_size) const
429 {
430         if (pimpl_->fontlist.empty())
431                 return def_size;
432
433         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
434         Pimpl::FontList::const_iterator const end = pimpl_->fontlist.end();
435         for (; end_it != end; ++end_it) {
436                 if (end_it->pos() >= endpos)
437                         break;
438         }
439
440         if (end_it != end)
441                 ++end_it;
442
443         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
444         for (; cit != end; ++cit) {
445                 if (cit->pos() >= startpos)
446                         break;
447         }
448
449         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
450         for (; cit != end_it; ++cit) {
451                 LyXFont::FONT_SIZE size = cit->font().size();
452                 if (size == LyXFont::INHERIT_SIZE)
453                         size = def_size;
454                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
455                         maxsize = size;
456         }
457         return maxsize;
458 }
459
460
461 Paragraph::value_type
462 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
463 {
464         value_type c = getChar(pos);
465         if (!lyxrc.rtl_support)
466                 return c;
467
468         value_type uc = c;
469         switch (c) {
470         case '(':
471                 uc = ')';
472                 break;
473         case ')':
474                 uc = '(';
475                 break;
476         case '[':
477                 uc = ']';
478                 break;
479         case ']':
480                 uc = '[';
481                 break;
482         case '{':
483                 uc = '}';
484                 break;
485         case '}':
486                 uc = '{';
487                 break;
488         case '<':
489                 uc = '>';
490                 break;
491         case '>':
492                 uc = '<';
493                 break;
494         }
495         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
496                 return uc;
497         else
498                 return c;
499 }
500
501
502 void Paragraph::setFont(pos_type pos, LyXFont const & font)
503 {
504         BOOST_ASSERT(pos <= size());
505
506         // First, reduce font against layout/label font
507         // Update: The setCharFont() routine in text2.C already
508         // reduces font, so we don't need to do that here. (Asger)
509         // No need to simplify this because it will disappear
510         // in a new kernel. (Asger)
511         // Next search font table
512
513         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
514         Pimpl::FontList::iterator it = beg;
515         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
516         for (; it != endit; ++it) {
517                 if (it->pos() >= pos)
518                         break;
519         }
520         unsigned int i = distance(beg, it);
521         bool notfound = (it == endit);
522
523         if (!notfound && pimpl_->fontlist[i].font() == font)
524                 return;
525
526         bool begin = pos == 0 || notfound ||
527                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
528         // Is position pos is a beginning of a font block?
529         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
530         // Is position pos is the end of a font block?
531         if (begin && end) { // A single char block
532                 if (i + 1 < pimpl_->fontlist.size() &&
533                     pimpl_->fontlist[i + 1].font() == font) {
534                         // Merge the singleton block with the next block
535                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
536                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
537                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
538                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
539                         // Merge the singleton block with the previous block
540                         pimpl_->fontlist[i - 1].pos(pos);
541                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
542                 } else
543                         pimpl_->fontlist[i].font(font);
544         } else if (begin) {
545                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
546                         pimpl_->fontlist[i - 1].pos(pos);
547                 else
548                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
549                                         Pimpl::FontTable(pos, font));
550         } else if (end) {
551                 pimpl_->fontlist[i].pos(pos - 1);
552                 if (!(i + 1 < pimpl_->fontlist.size() &&
553                       pimpl_->fontlist[i + 1].font() == font))
554                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
555                                         Pimpl::FontTable(pos, font));
556         } else { // The general case. The block is splitted into 3 blocks
557                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
558                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
559                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
560                                 Pimpl::FontTable(pos, font));
561         }
562 }
563
564
565 void Paragraph::makeSameLayout(Paragraph const & par)
566 {
567         layout(par.layout());
568         // move to pimpl?
569         params() = par.params();
570 }
571
572
573 int Paragraph::stripLeadingSpaces()
574 {
575         if (isFreeSpacing())
576                 return 0;
577
578         int i = 0;
579         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
580                 pimpl_->eraseIntern(0);
581                 ++i;
582         }
583
584         return i;
585 }
586
587
588 bool Paragraph::hasSameLayout(Paragraph const & par) const
589 {
590         return par.layout() == layout() && params().sameLayout(par.params());
591 }
592
593
594 Paragraph::depth_type Paragraph::getDepth() const
595 {
596         return params().depth();
597 }
598
599
600 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
601 {
602         if (layout()->isEnvironment())
603                 return params().depth() + 1;
604         else
605                 return params().depth();
606 }
607
608
609 char Paragraph::getAlign() const
610 {
611         return params().align();
612 }
613
614
615 string const & Paragraph::getLabelstring() const
616 {
617         return params().labelString();
618 }
619
620
621 // the next two functions are for the manual labels
622 string const Paragraph::getLabelWidthString() const
623 {
624         if (!params().labelWidthString().empty())
625                 return params().labelWidthString();
626         else
627                 return _("Senseless with this layout!");
628 }
629
630
631 void Paragraph::setLabelWidthString(string const & s)
632 {
633         params().labelWidthString(s);
634 }
635
636
637 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
638 {
639         layout(new_layout);
640         params().labelWidthString(string());
641         params().align(LYX_ALIGN_LAYOUT);
642         params().spacing(Spacing(Spacing::Default));
643 }
644
645
646 pos_type Paragraph::beginOfBody() const
647 {
648         return begin_of_body_;
649 }
650
651
652 void Paragraph::setBeginOfBody()
653 {
654         if (layout()->labeltype != LABEL_MANUAL) {
655                 begin_of_body_ = 0;
656                 return;
657         }
658
659         // Unroll the first two cycles of the loop
660         // and remember the previous character to
661         // remove unnecessary getChar() calls
662         pos_type i = 0;
663         pos_type end = size();
664         if (i < end && !isNewline(i)) {
665                 ++i;
666                 char previous_char = 0;
667                 char temp = 0;
668                 if (i < end) {
669                         previous_char = text_[i];
670                         if (!isNewline(i)) {
671                                 ++i;
672                                 while (i < end && previous_char != ' ') {
673                                         temp = text_[i];
674                                         if (isNewline(i))
675                                                 break;
676                                         ++i;
677                                         previous_char = temp;
678                                 }
679                         }
680                 }
681         }
682
683         begin_of_body_ = i;
684 }
685
686
687 // returns -1 if inset not found
688 int Paragraph::getPositionOfInset(InsetBase const * inset) const
689 {
690         // Find the entry.
691         InsetList::const_iterator it = insetlist.begin();
692         InsetList::const_iterator end = insetlist.end();
693         for (; it != end; ++it)
694                 if (it->inset == inset)
695                         return it->pos;
696         return -1;
697 }
698
699
700 InsetBibitem * Paragraph::bibitem() const
701 {
702         if (!insetlist.empty()) {
703                 InsetBase * inset = insetlist.begin()->inset;
704                 if (inset->lyxCode() == InsetBase::BIBTEX_CODE)
705                         return static_cast<InsetBibitem *>(inset);
706         }
707         return 0;
708 }
709
710
711 bool Paragraph::forceDefaultParagraphs() const
712 {
713         return inInset() && inInset()->forceDefaultParagraphs(inInset());
714 }
715
716
717 namespace {
718
719 // paragraphs inside floats need different alignment tags to avoid
720 // unwanted space
721
722 bool noTrivlistCentering(InsetBase::Code code)
723 {
724         return code == InsetBase::FLOAT_CODE || code == InsetBase::WRAP_CODE;
725 }
726
727
728 string correction(string const & orig)
729 {
730         if (orig == "flushleft")
731                 return "raggedright";
732         if (orig == "flushright")
733                 return "raggedleft";
734         if (orig == "center")
735                 return "centering";
736         return orig;
737 }
738
739
740 string const corrected_env(string const & suffix, string const & env,
741         InsetBase::Code code)
742 {
743         string output = suffix + "{";
744         if (noTrivlistCentering(code))
745                 output += correction(env);
746         else
747                 output += env;
748         return output + "}";
749 }
750
751 } // namespace anon
752
753
754 // This could go to ParagraphParameters if we want to
755 int Paragraph::startTeXParParams(BufferParams const & bparams,
756                                  ostream & os, bool moving_arg) const
757 {
758         int column = 0;
759
760         if (params().noindent()) {
761                 os << "\\noindent ";
762                 column += 10;
763         }
764
765         switch (params().align()) {
766         case LYX_ALIGN_NONE:
767         case LYX_ALIGN_BLOCK:
768         case LYX_ALIGN_LAYOUT:
769         case LYX_ALIGN_SPECIAL:
770                 break;
771         case LYX_ALIGN_LEFT:
772         case LYX_ALIGN_RIGHT:
773         case LYX_ALIGN_CENTER:
774                 if (moving_arg) {
775                         os << "\\protect";
776                         column += 8;
777                 }
778                 break;
779         }
780
781         switch (params().align()) {
782         case LYX_ALIGN_NONE:
783         case LYX_ALIGN_BLOCK:
784         case LYX_ALIGN_LAYOUT:
785         case LYX_ALIGN_SPECIAL:
786                 break;
787         case LYX_ALIGN_LEFT: {
788                 string output;
789                 if (getParLanguage(bparams)->babel() != "hebrew")
790                         output = corrected_env("\\begin", "flushleft", ownerCode());
791                 else
792                         output = corrected_env("\\begin", "flushright", ownerCode());
793                 os << output;
794                 column += output.size();
795                 break;
796         } case LYX_ALIGN_RIGHT: {
797                 string output;
798                 if (getParLanguage(bparams)->babel() != "hebrew")
799                         output = corrected_env("\\begin", "flushright", ownerCode());
800                 else
801                         output = corrected_env("\\begin", "flushleft", ownerCode());
802                 os << output;
803                 column += output.size();
804                 break;
805         } case LYX_ALIGN_CENTER: {
806                 string output;
807                 output = corrected_env("\\begin", "center", ownerCode());
808                 os << output;
809                 column += output.size();
810                 break;
811         }
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                 string output;
848                 if (getParLanguage(bparams)->babel() != "hebrew")
849                         output = corrected_env("\\par\\end", "flushleft", ownerCode());
850                 else
851                         output = corrected_env("\\par\\end", "flushright", ownerCode());
852                 os << output;
853                 column += output.size();
854                 break;
855         } case LYX_ALIGN_RIGHT: {
856                 string output;
857                 if (getParLanguage(bparams)->babel() != "hebrew")
858                         output = corrected_env("\\par\\end", "flushright", ownerCode());
859                 else
860                         output = corrected_env("\\par\\end", "flushleft", ownerCode());
861                 os << output;
862                 column += output.size();
863                 break;
864         } case LYX_ALIGN_CENTER: {
865                 string output;
866                 output = corrected_env("\\par\\end", "center", ownerCode());
867                 os << output;
868                 column += output.size();
869                 break;
870         }
871         }
872
873         return column;
874 }
875
876
877 // This one spits out the text of the paragraph
878 bool Paragraph::simpleTeXOnePar(Buffer const & buf,
879                                 BufferParams const & bparams,
880                                 LyXFont const & outerfont,
881                                 ostream & os, TexRow & texrow,
882                                 OutputParams const & runparams) const
883 {
884         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
885
886         bool return_value = false;
887
888         LyXLayout_ptr style;
889
890         // well we have to check if we are in an inset with unlimited
891         // length (all in one row) if that is true then we don't allow
892         // any special options in the paragraph and also we don't allow
893         // any environment other then "Standard" to be valid!
894         bool asdefault = forceDefaultParagraphs();
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 = beginOfBody();
906         unsigned int column = 0;
907
908         if (body_pos > 0) {
909                 // the optional argument is kept in curly brackets in
910                 // case it contains a ']'
911                 os << "[{";
912                 column += 2;
913                 basefont = getLabelFont(bparams, outerfont);
914         } else {
915                 basefont = getLayoutFont(bparams, outerfont);
916         }
917
918         // Which font is currently active?
919         LyXFont running_font(basefont);
920         // Do we have an open font change?
921         bool open_font = false;
922
923         Change::Type running_change = Change::UNCHANGED;
924
925         texrow.start(id(), 0);
926
927         // if the paragraph is empty, the loop will not be entered at all
928         if (empty()) {
929                 if (style->isCommand()) {
930                         os << '{';
931                         ++column;
932                 }
933                 if (!asdefault)
934                         column += startTeXParParams(bparams, os,
935                                                     runparams.moving_arg);
936         }
937
938         for (pos_type i = 0; i < size(); ++i) {
939                 ++column;
940                 // First char in paragraph or after label?
941                 if (i == body_pos) {
942                         if (body_pos > 0) {
943                                 if (open_font) {
944                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
945                                         open_font = false;
946                                 }
947                                 basefont = getLayoutFont(bparams, outerfont);
948                                 running_font = basefont;
949                                 os << "}] ";
950                                 column +=3;
951                         }
952                         if (style->isCommand()) {
953                                 os << '{';
954                                 ++column;
955                         }
956
957                         if (!asdefault)
958                                 column += startTeXParParams(bparams, os,
959                                                             runparams.moving_arg);
960                 }
961
962                 value_type c = getChar(i);
963
964                 // Fully instantiated font
965                 LyXFont font = getFont(bparams, i, outerfont);
966
967                 LyXFont const last_font = running_font;
968
969                 // Spaces at end of font change are simulated to be
970                 // outside font change, i.e. we write "\textXX{text} "
971                 // rather than "\textXX{text }". (Asger)
972                 if (open_font && c == ' ' && i <= size() - 2) {
973                         LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
974                         if (next_font != running_font && next_font != font) {
975                                 font = next_font;
976                         }
977                 }
978
979                 // We end font definition before blanks
980                 if (open_font &&
981                     (font != running_font ||
982                      font.language() != running_font.language()))
983                 {
984                         column += running_font.latexWriteEndChanges(os,
985                                                                     basefont,
986                                                                     (i == body_pos-1) ? basefont : font);
987                         running_font = basefont;
988                         open_font = false;
989                 }
990
991                 // Blanks are printed before start of fontswitch
992                 if (c == ' ') {
993                         // Do not print the separation of the optional argument
994                         if (i != body_pos - 1) {
995                                 pimpl_->simpleTeXBlanks(os, texrow, i,
996                                                        column, font, *style);
997                         }
998                 }
999
1000                 // Do we need to change font?
1001                 if ((font != running_font ||
1002                      font.language() != running_font.language()) &&
1003                         i != body_pos - 1)
1004                 {
1005                         column += font.latexWriteStartChanges(os, basefont,
1006                                                               last_font);
1007                         running_font = font;
1008                         open_font = true;
1009                 }
1010
1011                 Change::Type change = pimpl_->lookupChange(i);
1012
1013                 column += Changes::latexMarkChange(os, running_change, change);
1014                 running_change = change;
1015
1016                 OutputParams rp = runparams;
1017                 rp.free_spacing = style->free_spacing;
1018                 rp.local_language = font.language()->babel();
1019                 rp.intitle = style->intitle;
1020                 pimpl_->simpleTeXSpecialChars(buf, bparams,
1021                                               os, texrow, rp,
1022                                               font, running_font,
1023                                               basefont, outerfont, open_font,
1024                                               running_change,
1025                                               *style, i, column, c);
1026         }
1027
1028         column += Changes::latexMarkChange(os,
1029                         running_change, Change::UNCHANGED);
1030
1031         // If we have an open font definition, we have to close it
1032         if (open_font) {
1033 #ifdef FIXED_LANGUAGE_END_DETECTION
1034                 if (next_) {
1035                         running_font
1036                                 .latexWriteEndChanges(os, basefont,
1037                                                       next_->getFont(bparams,
1038                                                       0, outerfont));
1039                 } else {
1040                         running_font.latexWriteEndChanges(os, basefont,
1041                                                           basefont);
1042                 }
1043 #else
1044 #ifdef WITH_WARNINGS
1045 //#warning For now we ALWAYS have to close the foreign font settings if they are
1046 //#warning there as we start another \selectlanguage with the next paragraph if
1047 //#warning we are in need of this. This should be fixed sometime (Jug)
1048 #endif
1049                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1050 #endif
1051         }
1052
1053         // Needed if there is an optional argument but no contents.
1054         if (body_pos > 0 && body_pos == size()) {
1055                 os << "]~";
1056                 return_value = false;
1057         }
1058
1059         if (!asdefault) {
1060                 column += endTeXParParams(bparams, os, runparams.moving_arg);
1061         }
1062
1063         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1064         return return_value;
1065 }
1066
1067
1068 namespace {
1069
1070 // checks, if newcol chars should be put into this line
1071 // writes newline, if necessary.
1072 void sgmlLineBreak(ostream & os, string::size_type & colcount,
1073                           string::size_type newcol)
1074 {
1075         colcount += newcol;
1076         if (colcount > lyxrc.ascii_linelen) {
1077                 os << "\n";
1078                 colcount = newcol; // assume write after this call
1079         }
1080 }
1081
1082 enum PAR_TAG {
1083         PAR_NONE=0,
1084         TT = 1,
1085         SF = 2,
1086         BF = 4,
1087         IT = 8,
1088         SL = 16,
1089         EM = 32
1090 };
1091
1092
1093 string tag_name(PAR_TAG const & pt) {
1094         switch (pt) {
1095         case PAR_NONE: return "!-- --";
1096         case TT: return "tt";
1097         case SF: return "sf";
1098         case BF: return "bf";
1099         case IT: return "it";
1100         case SL: return "sl";
1101         case EM: return "em";
1102         }
1103         return "";
1104 }
1105
1106
1107 inline
1108 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
1109 {
1110         p1 = static_cast<PAR_TAG>(p1 | p2);
1111 }
1112
1113
1114 inline
1115 void reset(PAR_TAG & p1, PAR_TAG const & p2)
1116 {
1117         p1 = static_cast<PAR_TAG>(p1 & ~p2);
1118 }
1119
1120 } // anon
1121
1122
1123 // Handle internal paragraph parsing -- layout already processed.
1124 void Paragraph::simpleLinuxDocOnePar(Buffer const & buf,
1125                                      ostream & os,
1126                                      LyXFont const & outerfont,
1127                                      OutputParams const & runparams,
1128                                      lyx::depth_type /*depth*/) const
1129 {
1130         LyXLayout_ptr const & style = layout();
1131
1132         string::size_type char_line_count = 5;     // Heuristic choice ;-)
1133
1134         // gets paragraph main font
1135         LyXFont font_old;
1136         bool desc_on;
1137         if (style->labeltype == LABEL_MANUAL) {
1138                 font_old = style->labelfont;
1139                 desc_on = true;
1140         } else {
1141                 font_old = style->font;
1142                 desc_on = false;
1143         }
1144
1145         LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
1146         LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
1147         LyXFont::FONT_SHAPE  shape_type  = LyXFont::UP_SHAPE;
1148         bool is_em = false;
1149
1150         stack<PAR_TAG> tag_state;
1151         // parsing main loop
1152         for (pos_type i = 0; i < size(); ++i) {
1153
1154                 PAR_TAG tag_close = PAR_NONE;
1155                 list < PAR_TAG > tag_open;
1156
1157                 LyXFont const font = getFont(buf.params(), i, outerfont);
1158
1159                 if (font_old.family() != font.family()) {
1160                         switch (family_type) {
1161                         case LyXFont::SANS_FAMILY:
1162                                 tag_close |= SF;
1163                                 break;
1164                         case LyXFont::TYPEWRITER_FAMILY:
1165                                 tag_close |= TT;
1166                                 break;
1167                         default:
1168                                 break;
1169                         }
1170
1171                         family_type = font.family();
1172
1173                         switch (family_type) {
1174                         case LyXFont::SANS_FAMILY:
1175                                 tag_open.push_back(SF);
1176                                 break;
1177                         case LyXFont::TYPEWRITER_FAMILY:
1178                                 tag_open.push_back(TT);
1179                                 break;
1180                         default:
1181                                 break;
1182                         }
1183                 }
1184
1185                 if (font_old.series() != font.series()) {
1186                         switch (series_type) {
1187                         case LyXFont::BOLD_SERIES:
1188                                 tag_close |= BF;
1189                                 break;
1190                         default:
1191                                 break;
1192                         }
1193
1194                         series_type = font.series();
1195
1196                         switch (series_type) {
1197                         case LyXFont::BOLD_SERIES:
1198                                 tag_open.push_back(BF);
1199                                 break;
1200                         default:
1201                                 break;
1202                         }
1203
1204                 }
1205
1206                 if (font_old.shape() != font.shape()) {
1207                         switch (shape_type) {
1208                         case LyXFont::ITALIC_SHAPE:
1209                                 tag_close |= IT;
1210                                 break;
1211                         case LyXFont::SLANTED_SHAPE:
1212                                 tag_close |= SL;
1213                                 break;
1214                         default:
1215                                 break;
1216                         }
1217
1218                         shape_type = font.shape();
1219
1220                         switch (shape_type) {
1221                         case LyXFont::ITALIC_SHAPE:
1222                                 tag_open.push_back(IT);
1223                                 break;
1224                         case LyXFont::SLANTED_SHAPE:
1225                                 tag_open.push_back(SL);
1226                                 break;
1227                         default:
1228                                 break;
1229                         }
1230                 }
1231                 // handle <em> tag
1232                 if (font_old.emph() != font.emph()) {
1233                         if (font.emph() == LyXFont::ON) {
1234                                 tag_open.push_back(EM);
1235                                 is_em = true;
1236                         }
1237                         else if (is_em) {
1238                                 tag_close |= EM;
1239                                 is_em = false;
1240                         }
1241                 }
1242
1243                 list < PAR_TAG > temp;
1244                 while (!tag_state.empty() && tag_close) {
1245                         PAR_TAG k =  tag_state.top();
1246                         tag_state.pop();
1247                         os << "</" << tag_name(k) << '>';
1248                         if (tag_close & k)
1249                                 reset(tag_close,k);
1250                         else
1251                                 temp.push_back(k);
1252                 }
1253
1254                 for(list< PAR_TAG >::const_iterator j = temp.begin();
1255                     j != temp.end(); ++j) {
1256                         tag_state.push(*j);
1257                         os << '<' << tag_name(*j) << '>';
1258                 }
1259
1260                 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
1261                     j != tag_open.end(); ++j) {
1262                         tag_state.push(*j);
1263                         os << '<' << tag_name(*j) << '>';
1264                 }
1265
1266                 value_type c = getChar(i);
1267
1268
1269                 if (c == Paragraph::META_INSET) {
1270                         getInset(i)->linuxdoc(buf, os, runparams);
1271                         font_old = font;
1272                         continue;
1273                 }
1274
1275                 if (style->latexparam() == "CDATA") {
1276                         // "TeX"-Mode on == > SGML-Mode on.
1277                         if (c != '\0')
1278                                 os << c;
1279                         ++char_line_count;
1280                 } else {
1281                         bool ws;
1282                         string str;
1283                         boost::tie(ws, str) = sgml::escapeChar(c);
1284                         if (ws && !isFreeSpacing()) {
1285                                 // in freespacing mode, spaces are
1286                                 // non-breaking characters
1287                                 if (desc_on) { // if char is ' ' then...
1288                                         ++char_line_count;
1289                                         sgmlLineBreak(os, char_line_count, 6);
1290                                         os << "</tag>";
1291                                         desc_on = false;
1292                                 } else  {
1293                                         sgmlLineBreak(os, char_line_count, 1);
1294                                         os << c;
1295                                 }
1296                         } else {
1297                                 os << str;
1298                                 char_line_count += str.length();
1299                         }
1300                 }
1301                 font_old = font;
1302         }
1303
1304         while (!tag_state.empty()) {
1305                 os << "</" << tag_name(tag_state.top()) << '>';
1306                 tag_state.pop();
1307         }
1308
1309         // resets description flag correctly
1310         if (desc_on) {
1311                 // <tag> not closed...
1312                 sgmlLineBreak(os, char_line_count, 6);
1313                 os << "</tag>";
1314         }
1315 }
1316
1317
1318 bool Paragraph::emptyTag() const
1319 {
1320         for (pos_type i = 0; i < size(); ++i) {
1321                 if (isInset(i)) {
1322                         InsetBase const * inset = getInset(i);
1323                         InsetBase::Code lyx_code = inset->lyxCode();
1324                         if (lyx_code != InsetBase::TOC_CODE &&
1325                             lyx_code != InsetBase::INCLUDE_CODE &&
1326                             lyx_code != InsetBase::GRAPHICS_CODE &&
1327                             lyx_code != InsetBase::ERT_CODE &&
1328                             lyx_code != InsetBase::FLOAT_CODE &&
1329                             lyx_code != InsetBase::TABULAR_CODE) {
1330                                 return false;
1331                         }
1332                 } else {
1333                         value_type c = getChar(i);
1334                         if (c != ' ' && c != '\t')
1335                                 return false;
1336                 }
1337         }
1338         return true;
1339 }
1340
1341
1342 string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) const
1343 {
1344         for (pos_type i = 0; i < size(); ++i) {
1345                 if (isInset(i)) {
1346                         InsetBase const * inset = getInset(i);
1347                         InsetBase::Code lyx_code = inset->lyxCode();
1348                         if (lyx_code == InsetBase::LABEL_CODE) {
1349                                 string const id = static_cast<InsetCommand const *>(inset)->getContents();
1350                                 return "id=\"" + sgml::cleanID(buf, runparams, id) + "\"";
1351                         }
1352                 }
1353
1354         }
1355         return string();
1356 }
1357
1358
1359 pos_type Paragraph::getFirstWord(Buffer const & buf, ostream & os, OutputParams const & runparams) const
1360 {
1361         pos_type i;
1362         for (i = 0; i < size(); ++i) {
1363                 if (isInset(i)) {
1364                         InsetBase const * inset = getInset(i);
1365                         inset->docbook(buf, os, runparams);
1366                 } else {
1367                         value_type c = getChar(i);
1368                         if (c == ' ')
1369                                 break;
1370                         bool ws;
1371                         string str;
1372                         boost::tie(ws, str) = sgml::escapeChar(c);
1373
1374                         os << str;
1375                 }
1376         }
1377         return i;
1378 }
1379
1380
1381 bool Paragraph::onlyText(Buffer const & buf, LyXFont const & outerfont, pos_type initial) const
1382 {
1383         LyXFont font_old;
1384
1385         for (pos_type i = initial; i < size(); ++i) {
1386                 LyXFont font = getFont(buf.params(), i, outerfont);
1387                 if (isInset(i))
1388                         return false;
1389                 if (i != initial && font != font_old)
1390                         return false;
1391                 font_old = font;
1392         }
1393
1394         return true;
1395 }
1396
1397
1398 void Paragraph::simpleDocBookOnePar(Buffer const & buf,
1399                                     ostream & os,
1400                                     OutputParams const & runparams,
1401                                     LyXFont const & outerfont,
1402                                     pos_type initial) const
1403 {
1404         bool emph_flag = false;
1405
1406         LyXLayout_ptr const & style = layout();
1407         LyXFont font_old =
1408                 style->labeltype == LABEL_MANUAL ? style->labelfont : style->font;
1409
1410         if (style->pass_thru && !onlyText(buf, outerfont, initial))
1411                 os << "]]>";
1412
1413         // parsing main loop
1414         for (pos_type i = initial; i < size(); ++i) {
1415                 LyXFont font = getFont(buf.params(), i, outerfont);
1416
1417                 // handle <emphasis> tag
1418                 if (font_old.emph() != font.emph()) {
1419                         if (font.emph() == LyXFont::ON) {
1420                                 os << "<emphasis>";
1421                                 emph_flag = true;
1422                         } else if (i != initial) {
1423                                 os << "</emphasis>";
1424                                 emph_flag = false;
1425                         }
1426                 }
1427
1428                 if (isInset(i)) {
1429                         InsetBase const * inset = getInset(i);
1430                         inset->docbook(buf, os, runparams);
1431                 } else {
1432                         value_type c = getChar(i);
1433                         bool ws;
1434                         string str;
1435                         boost::tie(ws, str) = sgml::escapeChar(c);
1436
1437                         if (style->pass_thru)
1438                                 os << c;
1439                         else
1440                                 os << str;
1441                 }
1442                 font_old = font;
1443         }
1444
1445         if (emph_flag) {
1446                 os << "</emphasis>";
1447         }
1448
1449         if (style->free_spacing)
1450                 os << '\n';
1451         if (style->pass_thru && !onlyText(buf, outerfont, initial))
1452                 os << "<![CDATA[";
1453 }
1454
1455
1456 namespace {
1457
1458 /// return true if the char is a meta-character for an inset
1459 inline
1460 bool IsInsetChar(char c)
1461 {
1462         return (c == Paragraph::META_INSET);
1463 }
1464
1465 } // namespace anon
1466
1467
1468
1469 bool Paragraph::isHfill(pos_type pos) const
1470 {
1471         return isInset(pos)
1472                 && getInset(pos)->lyxCode() == InsetBase::HFILL_CODE;
1473 }
1474
1475
1476 bool Paragraph::isNewline(pos_type pos) const
1477 {
1478         return isInset(pos)
1479                 && getInset(pos)->lyxCode() == InsetBase::NEWLINE_CODE;
1480 }
1481
1482
1483 bool Paragraph::isSeparator(pos_type pos) const
1484 {
1485         return IsSeparatorChar(getChar(pos));
1486 }
1487
1488
1489 bool Paragraph::isLineSeparator(pos_type pos) const
1490 {
1491         value_type const c = getChar(pos);
1492         return IsLineSeparatorChar(c)
1493                 || (IsInsetChar(c) && getInset(pos) &&
1494                 getInset(pos)->isLineSeparator());
1495 }
1496
1497
1498 /// Used by the spellchecker
1499 bool Paragraph::isLetter(pos_type pos) const
1500 {
1501         if (isInset(pos))
1502                 return getInset(pos)->isLetter();
1503         else {
1504                 value_type const c = getChar(pos);
1505                 return IsLetterChar(c) || IsDigit(c);
1506         }
1507 }
1508
1509
1510 Language const *
1511 Paragraph::getParLanguage(BufferParams const & bparams) const
1512 {
1513         if (!empty())
1514                 return getFirstFontSettings().language();
1515 #ifdef WITH_WARNINGS
1516 #warning FIXME we should check the prev par as well (Lgb)
1517 #endif
1518         return bparams.language;
1519 }
1520
1521
1522 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1523 {
1524         return lyxrc.rtl_support
1525                 && getParLanguage(bparams)->RightToLeft()
1526                 && ownerCode() != InsetBase::ERT_CODE;
1527 }
1528
1529
1530 void Paragraph::changeLanguage(BufferParams const & bparams,
1531                                Language const * from, Language const * to)
1532 {
1533         for (pos_type i = 0; i < size(); ++i) {
1534                 LyXFont font = getFontSettings(bparams, i);
1535                 if (font.language() == from) {
1536                         font.setLanguage(to);
1537                         setFont(i, font);
1538                 }
1539         }
1540 }
1541
1542
1543 bool Paragraph::isMultiLingual(BufferParams const & bparams) const
1544 {
1545         Language const * doc_language = bparams.language;
1546         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1547         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1548
1549         for (; cit != end; ++cit)
1550                 if (cit->font().language() != ignore_language &&
1551                     cit->font().language() != latex_language &&
1552                     cit->font().language() != doc_language)
1553                         return true;
1554         return false;
1555 }
1556
1557
1558 // Convert the paragraph to a string.
1559 // Used for building the table of contents
1560 string const Paragraph::asString(Buffer const & buffer, bool label) const
1561 {
1562         OutputParams runparams;
1563         return asString(buffer, runparams, label);
1564 }
1565
1566
1567 string const Paragraph::asString(Buffer const & buffer,
1568                                  OutputParams const & runparams,
1569                                  bool label) const
1570 {
1571 #if 0
1572         string s;
1573         if (label && !params().labelString().empty())
1574                 s += params().labelString() + ' ';
1575
1576         for (pos_type i = 0; i < size(); ++i) {
1577                 value_type c = getChar(i);
1578                 if (IsPrintable(c))
1579                         s += c;
1580                 else if (c == META_INSET &&
1581                          getInset(i)->lyxCode() == InsetBase::MATH_CODE) {
1582                         ostringstream os;
1583                         getInset(i)->plaintext(buffer, os, runparams);
1584                         s += subst(STRCONV(os.str()),'\n',' ');
1585                 }
1586         }
1587
1588         return s;
1589 #else
1590         // This should really be done by the caller and not here.
1591         string ret = asString(buffer, runparams, 0, size(), label);
1592         return subst(ret, '\n', ' ');
1593 #endif
1594 }
1595
1596
1597 string const Paragraph::asString(Buffer const & buffer,
1598                                  pos_type beg, pos_type end, bool label) const
1599 {
1600
1601         OutputParams const runparams;
1602         return asString(buffer, runparams, beg, end, label);
1603 }
1604
1605
1606 string const Paragraph::asString(Buffer const & buffer,
1607                                  OutputParams const & runparams,
1608                                  pos_type beg, pos_type end, bool label) const
1609 {
1610         ostringstream os;
1611
1612         if (beg == 0 && label && !params().labelString().empty())
1613                 os << params().labelString() << ' ';
1614
1615         for (pos_type i = beg; i < end; ++i) {
1616                 value_type const c = getUChar(buffer.params(), i);
1617                 if (IsPrintable(c))
1618                         os << c;
1619                 else if (c == META_INSET)
1620                         getInset(i)->plaintext(buffer, os, runparams);
1621         }
1622
1623         return os.str();
1624 }
1625
1626
1627 void Paragraph::setInsetOwner(UpdatableInset * inset)
1628 {
1629         pimpl_->inset_owner = inset;
1630 }
1631
1632
1633 void Paragraph::setContentsFromPar(Paragraph const & par)
1634 {
1635         pimpl_->setContentsFromPar(par);
1636 }
1637
1638
1639 void Paragraph::trackChanges(Change::Type type)
1640 {
1641         pimpl_->trackChanges(type);
1642 }
1643
1644
1645 void Paragraph::untrackChanges()
1646 {
1647         pimpl_->untrackChanges();
1648 }
1649
1650
1651 void Paragraph::cleanChanges()
1652 {
1653         pimpl_->cleanChanges();
1654 }
1655
1656
1657 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1658 {
1659         BOOST_ASSERT(empty() || pos < size());
1660         return pimpl_->lookupChange(pos);
1661 }
1662
1663
1664 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1665 {
1666         BOOST_ASSERT(empty() || pos < size());
1667         return pimpl_->lookupChangeFull(pos);
1668 }
1669
1670
1671 bool Paragraph::isChanged(pos_type start, pos_type end) const
1672 {
1673         return pimpl_->isChanged(start, end);
1674 }
1675
1676
1677 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1678 {
1679         return pimpl_->isChangeEdited(start, end);
1680 }
1681
1682
1683 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1684 {
1685         pimpl_->setChange(pos, type);
1686 }
1687
1688
1689 void Paragraph::markErased()
1690 {
1691         pimpl_->markErased();
1692 }
1693
1694
1695 void Paragraph::acceptChange(pos_type start, pos_type end)
1696 {
1697         return pimpl_->acceptChange(start, end);
1698 }
1699
1700
1701 void Paragraph::rejectChange(pos_type start, pos_type end)
1702 {
1703         return pimpl_->rejectChange(start, end);
1704 }
1705
1706
1707 int Paragraph::id() const
1708 {
1709         return pimpl_->id_;
1710 }
1711
1712
1713 LyXLayout_ptr const & Paragraph::layout() const
1714 {
1715         return layout_;
1716 }
1717
1718
1719 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1720 {
1721         layout_ = new_layout;
1722 }
1723
1724
1725 UpdatableInset * Paragraph::inInset() const
1726 {
1727         return pimpl_->inset_owner;
1728 }
1729
1730
1731 InsetBase::Code Paragraph::ownerCode() const
1732 {
1733         return pimpl_->inset_owner
1734                 ? pimpl_->inset_owner->lyxCode() : InsetBase::NO_CODE;
1735 }
1736
1737
1738 void Paragraph::clearContents()
1739 {
1740         text_.clear();
1741 }
1742
1743
1744 void Paragraph::setChar(pos_type pos, value_type c)
1745 {
1746         text_[pos] = c;
1747 }
1748
1749
1750 ParagraphParameters & Paragraph::params()
1751 {
1752         return pimpl_->params;
1753 }
1754
1755
1756 ParagraphParameters const & Paragraph::params() const
1757 {
1758         return pimpl_->params;
1759 }
1760
1761
1762 bool Paragraph::isFreeSpacing() const
1763 {
1764         if (layout()->free_spacing)
1765                 return true;
1766
1767         // for now we just need this, later should we need this in some
1768         // other way we can always add a function to InsetBase too.
1769         return ownerCode() == InsetBase::ERT_CODE;
1770 }
1771
1772
1773 bool Paragraph::allowEmpty() const
1774 {
1775         if (layout()->keepempty)
1776                 return true;
1777         return ownerCode() == InsetBase::ERT_CODE;
1778 }
1779
1780
1781 Row & Paragraph::getRow(pos_type pos)
1782 {
1783         RowList::iterator rit = rows_.end();
1784         RowList::iterator const begin = rows_.begin();
1785
1786         for (--rit; rit != begin && rit->pos() > pos; --rit)
1787                 ;
1788
1789         return *rit;
1790 }
1791
1792
1793 Row const & Paragraph::getRow(pos_type pos) const
1794 {
1795         RowList::const_iterator rit = rows_.end();
1796         RowList::const_iterator const begin = rows_.begin();
1797
1798         for (--rit; rit != begin && rit->pos() > pos; --rit)
1799                 ;
1800
1801         return *rit;
1802 }
1803
1804
1805 size_t Paragraph::pos2row(pos_type pos) const
1806 {
1807         RowList::const_iterator rit = rows_.end();
1808         RowList::const_iterator const begin = rows_.begin();
1809
1810         for (--rit; rit != begin && rit->pos() > pos; --rit)
1811                 ;
1812
1813         return rit - begin;
1814 }
1815
1816
1817 unsigned char Paragraph::transformChar(unsigned char c, pos_type pos) const
1818 {
1819         if (!Encodings::is_arabic(c))
1820                 if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && IsDigit(c))
1821                         return c + (0xb0 - '0');
1822                 else
1823                         return c;
1824
1825         unsigned char const prev_char = pos > 0 ? getChar(pos - 1) : ' ';
1826         unsigned char next_char = ' ';
1827
1828         for (pos_type i = pos + 1, end = size(); i < end; ++i) {
1829                 unsigned char const par_char = getChar(i);
1830                 if (!Encodings::IsComposeChar_arabic(par_char)) {
1831                         next_char = par_char;
1832                         break;
1833                 }
1834         }
1835
1836         if (Encodings::is_arabic(next_char)) {
1837                 if (Encodings::is_arabic(prev_char) &&
1838                         !Encodings::is_arabic_special(prev_char))
1839                         return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
1840                 else
1841                         return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
1842         } else {
1843                 if (Encodings::is_arabic(prev_char) &&
1844                         !Encodings::is_arabic_special(prev_char))
1845                         return Encodings::TransformChar(c, Encodings::FORM_FINAL);
1846                 else
1847                         return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
1848         }
1849 }
1850
1851
1852 void Paragraph::dump() const
1853 {
1854         lyxerr << "Paragraph::dump: rows.size(): " << rows_.size() << endl;
1855         for (size_t i = 0; i != rows_.size(); ++i) {
1856                 lyxerr << "  row " << i << ":   ";
1857                 rows_[i].dump();
1858         }
1859 }
1860
1861 //void Paragraph::metrics(MetricsInfo & mi, Dimension & dim, LyXText & text)
1862 //{
1863 //}
1864 //
1865 //
1866 //void draw(PainterInfo & pi, int x, int y, LyXText & text) const
1867 //{
1868 //}