]> git.lyx.org Git - lyx.git/blob - src/paragraph_pimpl.C
Fix natbib bug spotted by JMarc.
[lyx.git] / src / paragraph_pimpl.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 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include "paragraph_pimpl.h"
18 #include "LaTeXFeatures.h"
19 #include "texrow.h"
20 #include "language.h"
21 #include "bufferparams.h"
22 #include "encoding.h"
23 #include "lyxrc.h"
24 #include "debug.h"
25
26 #include "support/LAssert.h"
27
28 using lyx::pos_type;
29 using std::endl;
30 using std::ostream;
31 using std::upper_bound;
32 using std::lower_bound;
33
34 // Initialize static member.
35 ShareContainer<LyXFont> Paragraph::Pimpl::FontTable::container;
36 // Initialization of the counter for the paragraph id's,
37 unsigned int Paragraph::Pimpl::paragraph_id = 0;
38
39 namespace {
40
41 struct special_phrase {
42         string phrase;
43         string macro;
44         bool builtin;
45 };
46
47 special_phrase special_phrases[] = {
48         { "LyX", "\\LyX{}", false },
49         { "TeX", "\\TeX{}", true },
50         { "LaTeX2e", "\\LaTeXe{}", true },
51         { "LaTeX", "\\LaTeX{}", true },
52 };
53
54 size_t const phrases_nr = sizeof(special_phrases)/sizeof(special_phrase);
55
56 } // namespace anon
57
58
59 Paragraph::Pimpl::Pimpl(Paragraph * owner)
60         : owner_(owner)
61 {
62         inset_owner = 0;
63         id_ = paragraph_id++;
64 }
65
66
67 Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner, bool same_ids)
68         : params(p.params), owner_(owner)
69 {
70         inset_owner = p.inset_owner;
71         text = p.text;
72         fontlist = p.fontlist;
73         if (same_ids)
74                 id_ = p.id_;
75         else
76                 id_ = paragraph_id++;
77
78         if (p.tracking())
79                 changes_.reset(new Changes(*p.changes_.get()));
80 }
81
82
83 void Paragraph::Pimpl::clear()
84 {
85         text.clear();
86 #warning changes ? 
87 }
88
89
90 void Paragraph::Pimpl::setContentsFromPar(Paragraph const * par)
91 {
92         lyx::Assert(par);
93         text = par->pimpl_->text;
94         if (par->pimpl_->tracking()) {
95                 changes_.reset(new Changes(*(par->pimpl_->changes_.get())));
96         }
97 }
98
99
100 void Paragraph::Pimpl::trackChanges(Change::Type type)
101 {
102         if (tracking()) {
103                 lyxerr[Debug::CHANGES] << "already tracking for par " << id_ << endl;
104                 return;
105         }
106  
107         lyxerr[Debug::CHANGES] << "track changes for par "
108                 << id_ << " type " << type << endl;
109         changes_.reset(new Changes(type));
110         changes_->set(type, 0, size());
111 }
112
113  
114 void Paragraph::Pimpl::untrackChanges()
115 {
116         changes_.reset(0);
117 }
118
119  
120 void Paragraph::Pimpl::cleanChanges()
121 {
122         // if we're not tracking, we don't want to reset...
123         if (!tracking())
124                 return;
125
126         changes_.reset(new Changes(Change::INSERTED));
127         changes_->set(Change::INSERTED, 0, size());
128 }
129
130  
131 bool Paragraph::Pimpl::isChanged(pos_type start, pos_type end) const
132 {
133         if (!tracking())
134                 return false;
135
136         return changes_->isChange(start, end);
137 }
138
139
140 bool Paragraph::Pimpl::isChangeEdited(pos_type start, pos_type end) const
141 {
142         if (!tracking())
143                 return false;
144
145         return changes_->isChangeEdited(start, end);
146 }
147
148  
149 void Paragraph::Pimpl::setChange(pos_type pos, Change::Type type)
150 {
151         if (!tracking())
152                 return;
153
154         changes_->set(type, pos);
155 }
156
157  
158 Change::Type Paragraph::Pimpl::lookupChange(pos_type pos) const
159 {
160         if (!tracking())
161                 return Change::UNCHANGED;
162
163         return changes_->lookup(pos);
164 }
165  
166
167 Change const Paragraph::Pimpl::lookupChangeFull(pos_type pos) const
168 {
169         if (!tracking())
170                 return Change(Change::UNCHANGED);
171
172         return changes_->lookupFull(pos);
173 }
174  
175  
176 void Paragraph::Pimpl::markErased()
177 {
178         lyx::Assert(tracking());
179
180         // FIXME: we should actually remove INSERTED chars.
181         // difficult because owning insettexts/tabulars need
182         // to update themselves when rows etc. change
183         changes_->set(Change::DELETED, 0, size());
184         changes_->reset(Change::DELETED);
185 }
186
187  
188 void Paragraph::Pimpl::acceptChange(pos_type start, pos_type end)
189 {
190         if (!tracking())
191                 return;
192  
193         if (!size()) {
194                 changes_.reset(new Changes(Change::UNCHANGED));
195                 return;
196         }
197  
198         lyxerr << "acceptchange" << endl; 
199         pos_type i = start;
200
201         for (; i < end; ++i) {
202                 switch (lookupChange(i)) {
203                         case Change::UNCHANGED:
204                                 break;
205
206                         case Change::INSERTED:
207                                 changes_->set(Change::UNCHANGED, i);
208                                 break;
209
210                         case Change::DELETED:
211                                 eraseIntern(i);
212                                 changes_->erase(i);
213                                 --end;
214                                 --i;
215                                 break;
216                 }
217         }
218
219         lyxerr << "endacceptchange" << endl; 
220         changes_->reset(Change::UNCHANGED);
221 }
222
223
224 void Paragraph::Pimpl::rejectChange(pos_type start, pos_type end)
225 {
226         if (!tracking())
227                 return;
228  
229         if (!size()) {
230                 changes_.reset(new Changes(Change::UNCHANGED));
231                 return;
232         }
233  
234         pos_type i = start;
235
236         for (; i < end; ++i) {
237                 switch (lookupChange(i)) {
238                         case Change::UNCHANGED:
239                                 break;
240
241                         case Change::INSERTED:
242                                 eraseIntern(i);
243                                 changes_->erase(i);
244                                 --end;
245                                 --i;
246                                 break;
247
248                         case Change::DELETED:
249                                 changes_->set(Change::UNCHANGED, i);
250                                 break;
251                 }
252         }
253         changes_->reset(Change::UNCHANGED);
254 }
255
256  
257 Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
258 {
259         // This is in the critical path for loading!
260         pos_type const siz = size();
261         lyx::Assert(pos <= siz);
262         // This is stronger, and I belive that this is the assertion
263         // that we should really use. (Lgb)
264         //Assert(pos < size());
265
266         // Then this has no meaning. (Lgb)
267         if (!siz || pos == siz)
268                 return '\0';
269
270         return text[pos];
271 }
272
273
274 void Paragraph::Pimpl::setChar(pos_type pos, value_type c)
275 {
276 #warning changes
277         text[pos] = c;
278 }
279
280
281 void Paragraph::Pimpl::insertChar(pos_type pos, value_type c,
282                                   LyXFont const & font, Change change)
283 {
284         lyx::Assert(pos <= size());
285
286         if (tracking()) {
287                 changes_->record(change, pos);
288         }
289
290         // This is actually very common when parsing buffers (and
291         // maybe inserting ascii text)
292         if (pos == size()) {
293                 // when appending characters, no need to update tables
294                 text.push_back(c);
295                 owner_->setFont(pos, font);
296                 return;
297         }
298
299         text.insert(text.begin() + pos, c);
300
301         // Update the font table.
302         FontTable search_font(pos, LyXFont());
303         for (FontList::iterator it = lower_bound(fontlist.begin(),
304                                                       fontlist.end(),
305                                                       search_font, matchFT());
306              it != fontlist.end(); ++it)
307         {
308                 it->pos(it->pos() + 1);
309         }
310
311         // Update the insets
312         owner_->insetlist.increasePosAfterPos(pos);
313
314         owner_->setFont(pos, font);
315 }
316
317
318 void Paragraph::Pimpl::insertInset(pos_type pos,
319                                    Inset * inset, LyXFont const & font, Change change)
320 {
321         lyx::Assert(inset);
322         lyx::Assert(pos <= size());
323
324         insertChar(pos, META_INSET, font, change);
325         lyx::Assert(text[pos] == META_INSET);
326
327         // Add a new entry in the insetlist.
328         owner_->insetlist.insert(inset, pos);
329         inset->parOwner(owner_);
330
331         if (inset_owner)
332                 inset->setOwner(inset_owner);
333 }
334
335
336 bool Paragraph::Pimpl::erasePos(pos_type pos)
337 {
338         lyx::Assert(pos < size());
339
340         if (tracking()) {
341                 Change::Type changetype(changes_->lookup(pos));
342                 changes_->record(Change(Change::DELETED), pos);
343
344                 // only allow the actual removal if it was /new/ text
345                 if (changetype != Change::INSERTED) {
346                         if (text[pos] == Paragraph::META_INSET) { 
347                                 Inset * i(owner_->getInset(pos));
348                                 i->markErased();
349                         }
350                         return false;
351                 }
352         }
353
354         eraseIntern(pos);
355         return true;
356 }
357
358  
359 void Paragraph::Pimpl::eraseIntern(pos_type pos)
360 {
361         // if it is an inset, delete the inset entry
362         if (text[pos] == Paragraph::META_INSET) {
363                 owner_->insetlist.erase(pos);
364         }
365
366         text.erase(text.begin() + pos);
367
368         // Erase entries in the tables.
369         FontTable search_font(pos, LyXFont());
370
371         FontList::iterator it =
372                 lower_bound(fontlist.begin(),
373                             fontlist.end(),
374                             search_font, matchFT());
375         if (it != fontlist.end() && it->pos() == pos &&
376             (pos == 0 ||
377              (it != fontlist.begin()
378               && boost::prior(it)->pos() == pos - 1))) {
379                 // If it is a multi-character font
380                 // entry, we just make it smaller
381                 // (see update below), otherwise we
382                 // should delete it.
383                 unsigned int const i = it - fontlist.begin();
384                 fontlist.erase(fontlist.begin() + i);
385                 it = fontlist.begin() + i;
386                 if (i > 0 && i < fontlist.size() &&
387                     fontlist[i - 1].font() == fontlist[i].font()) {
388                         fontlist.erase(fontlist.begin() + i - 1);
389                         it = fontlist.begin() + i - 1;
390                 }
391         }
392
393         // Update all other entries.
394         FontList::iterator fend = fontlist.end();
395         for (; it != fend; ++it)
396                 it->pos(it->pos() - 1);
397
398         // Update the insetlist.
399         owner_->insetlist.decreasePosAfterPos(pos);
400 }
401
402
403 void Paragraph::Pimpl::erase(pos_type pos)
404 {
405         erasePos(pos);
406 }
407
408  
409 bool Paragraph::Pimpl::erase(pos_type start, pos_type end)
410 {
411         pos_type i = start;
412         pos_type count = end - start;
413         bool any_erased = false;
414
415         while (count) {
416                 if (!erasePos(i)) {
417                         ++i;
418                 } else {
419                         any_erased = true;
420                 } 
421                 --count;
422         }
423         return any_erased;
424 }
425
426  
427 void Paragraph::Pimpl::simpleTeXBlanks(ostream & os, TexRow & texrow,
428                                        pos_type const i,
429                                        unsigned int & column,
430                                        LyXFont const & font,
431                                        LyXLayout const & style)
432 {
433         if (style.pass_thru) return;
434         if (column > lyxrc.ascii_linelen
435             && i
436             && getChar(i - 1) != ' '
437             && (i < size() - 1)
438             // same in FreeSpacing mode
439             && !style.free_spacing
440                 && !owner_->isFreeSpacing()
441             // In typewriter mode, we want to avoid
442             // ! . ? : at the end of a line
443             && !(font.family() == LyXFont::TYPEWRITER_FAMILY
444                  && (getChar(i - 1) == '.'
445                      || getChar(i - 1) == '?'
446                      || getChar(i - 1) == ':'
447                      || getChar(i - 1) == '!'))) {
448                 os << '\n';
449                 texrow.newline();
450                 texrow.start(owner_, i + 1);
451                 column = 0;
452         } else if (style.free_spacing) {
453                 os << '~';
454         } else {
455                 os << ' ';
456         }
457 }
458
459
460 bool Paragraph::Pimpl::isTextAt(string const & str, pos_type pos) const
461 {
462         pos_type const len = str.length();
463
464         // is the paragraph large enough?
465         if (pos + len > size())
466                 return false;
467
468         // does the wanted text start at point?
469         for (string::size_type i = 0; i < str.length(); ++i) {
470                 if (str[i] != text[pos + i])
471                         return false;
472         }
473
474         // is there a font change in middle of the word?
475         FontList::const_iterator cit = fontlist.begin();
476         FontList::const_iterator end = fontlist.end();
477         for (; cit != end; ++cit) {
478                 if (cit->pos() >= pos)
479                         break;
480         }
481         if (cit != end && pos + len - 1 > cit->pos())
482                 return false;
483
484         return true;
485 }
486
487  
488 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
489                                              BufferParams const & bparams,
490                                              ostream & os,
491                                              TexRow & texrow,
492                                              bool moving_arg,
493                                              LyXFont & font,
494                                              LyXFont & running_font,
495                                              LyXFont & basefont,
496                                              bool & open_font,
497                                              Change::Type & running_change,
498                                              LyXLayout const & style,
499                                              pos_type & i,
500                                              unsigned int & column,
501                                              value_type const c)
502 {
503         if (style.pass_thru) {
504                 if (c != '\0') os << c;
505                 return;
506         }
507         // Two major modes:  LaTeX or plain
508         // Handle here those cases common to both modes
509         // and then split to handle the two modes separately.
510         switch (c) {
511         case Paragraph::META_INSET: {
512                 Inset * inset = owner_->getInset(i);
513
514                 // FIXME: remove this check
515                 if (!inset)
516                         break;
517  
518                 if (inset->isTextInset()) {
519                         column += Changes::latexMarkChange(os, running_change,
520                                 Change::UNCHANGED);
521                         running_change = Change::UNCHANGED;
522                 }
523  
524                 bool close = false;
525                 int const len = os.tellp();
526                 //ostream::pos_type const len = os.tellp();
527                 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
528                      || inset->lyxCode() == Inset::MATH_CODE
529                      || inset->lyxCode() == Inset::URL_CODE)
530                     && running_font.isRightToLeft()) {
531                         os << "\\L{";
532                         close = true;
533                 }
534
535 #ifdef WITH_WARNINGS
536 #warning Bug: we can have an empty font change here!
537 // if there has just been a font change, we are going to close it
538 // right now, which means stupid latex code like \textsf{}. AFAIK,
539 // this does not harm dvi output. A minor bug, thus (JMarc)
540 #endif
541                 // some insets cannot be inside a font change command
542                 if (open_font && inset->noFontChange()) {
543                         column +=running_font.
544                                 latexWriteEndChanges(os,
545                                                      basefont,
546                                                      basefont);
547                         open_font = false;
548                         basefont = owner_->getLayoutFont(bparams);
549                         running_font = basefont;
550                 }
551
552                 int tmp = inset->latex(buf, os, moving_arg,
553                                        style.free_spacing);
554
555                 if (close)
556                         os << '}';
557
558                 if (tmp) {
559                         for (int j = 0; j < tmp; ++j) {
560                                 texrow.newline();
561                         }
562                         texrow.start(owner_, i + 1);
563                         column = 0;
564                 } else {
565                         column += int(os.tellp()) - len;
566                 }
567         }
568         break;
569
570         case Paragraph::META_NEWLINE:
571                 if (open_font) {
572                         column += running_font.latexWriteEndChanges(os,
573                                                                     basefont,
574                                                                     basefont);
575                         open_font = false;
576                 }
577                 basefont = owner_->getLayoutFont(bparams);
578                 running_font = basefont;
579                 break;
580
581         case Paragraph::META_HFILL:
582                 os << "\\hfill{}";
583                 column += 7;
584                 break;
585
586         default:
587                 // And now for the special cases within each mode
588
589                 switch (c) {
590                 case '\\':
591                         os << "\\textbackslash{}";
592                         column += 15;
593                         break;
594
595                 case '±': case '²': case '³':
596                 case '×': case '÷': case '¹':
597                 case '¬': case 'µ':
598                         if ((bparams.inputenc == "latin1" ||
599                              bparams.inputenc == "latin9") ||
600                             (bparams.inputenc == "auto" &&
601                              (font.language()->encoding()->LatexName()
602                               == "latin1" ||
603                               font.language()->encoding()->LatexName()
604                               == "latin9"))) {
605                                 os << "\\ensuremath{"
606                                    << c
607                                    << '}';
608                                 column += 13;
609                         } else {
610                                 os << c;
611                         }
612                         break;
613
614                 case '|': case '<': case '>':
615                         // In T1 encoding, these characters exist
616                         if (lyxrc.fontenc == "T1") {
617                                 os << c;
618                                 //... but we should avoid ligatures
619                                 if ((c == '>' || c == '<')
620                                     && i <= size() - 2
621                                     && getChar(i + 1) == c) {
622                                         //os << "\\textcompwordmark{}";
623                                         // Jean-Marc, have a look at
624                                         // this. I think this works
625                                         // equally well:
626                                         os << "\\,{}";
627                                         // Lgb
628                                         column += 19;
629                                 }
630                                 break;
631                         }
632                         // Typewriter font also has them
633                         if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
634                                 os << c;
635                                 break;
636                         }
637                         // Otherwise, we use what LaTeX
638                         // provides us.
639                         switch (c) {
640                         case '<':
641                                 os << "\\textless{}";
642                                 column += 10;
643                                 break;
644                         case '>':
645                                 os << "\\textgreater{}";
646                                 column += 13;
647                                 break;
648                         case '|':
649                                 os << "\\textbar{}";
650                                 column += 9;
651                                 break;
652                         }
653                         break;
654
655                 case '-': // "--" in Typewriter mode -> "-{}-"
656                         if (i <= size() - 2
657                             && getChar(i + 1) == '-'
658                             && font.family() == LyXFont::TYPEWRITER_FAMILY) {
659                                 os << "-{}";
660                                 column += 2;
661                         } else {
662                                 os << '-';
663                         }
664                         break;
665
666                 case '\"':
667                         os << "\\char`\\\"{}";
668                         column += 9;
669                         break;
670
671                 case '£':
672                         if (bparams.inputenc == "default") {
673                                 os << "\\pounds{}";
674                                 column += 8;
675                         } else {
676                                 os << c;
677                         }
678                         break;
679
680                 case '$': case '&':
681                 case '%': case '#': case '{':
682                 case '}': case '_':
683                         os << '\\' << c;
684                         column += 1;
685                         break;
686
687                 case '~':
688                         os << "\\textasciitilde{}";
689                         column += 16;
690                         break;
691
692                 case '^':
693                         os << "\\textasciicircum{}";
694                         column += 17;
695                         break;
696
697                 case '*': case '[': case ']':
698                         // avoid being mistaken for optional arguments
699                         os << '{' << c << '}';
700                         column += 2;
701                         break;
702
703                 case ' ':
704                         // Blanks are printed before font switching.
705                         // Sure? I am not! (try nice-latex)
706                         // I am sure it's correct. LyX might be smarter
707                         // in the future, but for now, nothing wrong is
708                         // written. (Asger)
709                         break;
710
711                 default:
712
713                         // I assume this is hack treating typewriter as verbatim
714                         if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
715                                 if (c != '\0') {
716                                         os << c;
717                                 }
718                                 break;
719                         }
720
721                         // LyX, LaTeX etc.
722
723                         // FIXME: if we have "LaTeX" with a font
724                         // change in the middle (before the 'T', then
725                         // the "TeX" part is still special cased.
726                         // Really we should only operate this on
727                         // "words" for some definition of word
728
729                         size_t pnr = 0;
730
731                         for (; pnr < phrases_nr; ++pnr) {
732                                 if (isTextAt(special_phrases[pnr].phrase, i)) {
733                                         os << special_phrases[pnr].macro;
734                                         i += special_phrases[pnr].phrase.length() - 1;
735                                         column += special_phrases[pnr].macro.length() - 1;
736                                         break;
737                                 }
738                         }
739
740                         if (pnr == phrases_nr && c != '\0') {
741                                 os << c;
742                         }
743                         break;
744                 }
745         }
746 }
747
748
749
750 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
751                                         BufferParams const & bparams,
752                                         ostream & os, TexRow & texrow)
753 {
754         lyxerr[Debug::LATEX] << "TeXDeeper...     " << this << endl;
755         Paragraph * par = owner_;
756
757         while (par && par->params().depth() == owner_->params().depth()) {
758                 if (par->layout()->isEnvironment()) {
759                         par = par->TeXEnvironment(buf, bparams,
760                                                   os, texrow);
761                 } else {
762                         par = par->TeXOnePar(buf, bparams,
763                                              os, texrow, false);
764                 }
765         }
766         lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << endl;
767
768         return par;
769 }
770
771
772 void Paragraph::Pimpl::validate(LaTeXFeatures & features,
773                                 LyXLayout const & layout) const
774 {
775         BufferParams const & bparams = features.bufferParams();
776
777         // check the params.
778         if (params.lineTop() || params.lineBottom())
779                 features.require("lyxline");
780         if (!params.spacing().isDefault())
781                 features.require("setspace");
782
783         // then the layouts
784         features.useLayout(layout.name());
785
786         // then the fonts
787         Language const * doc_language = bparams.language;
788
789         FontList::const_iterator fcit = fontlist.begin();
790         FontList::const_iterator fend = fontlist.end();
791         for (; fcit != fend; ++fcit) {
792                 if (fcit->font().noun() == LyXFont::ON) {
793                         lyxerr[Debug::LATEX] << "font.noun: "
794                                              << fcit->font().noun()
795                                              << endl;
796                         features.require("noun");
797                         lyxerr[Debug::LATEX] << "Noun enabled. Font: "
798                                              << fcit->font().stateText(0)
799                                              << endl;
800                 }
801                 switch (fcit->font().color()) {
802                 case LColor::none:
803                 case LColor::inherit:
804                 case LColor::ignore:
805                         // probably we should put here all interface colors used for
806                         // font displaying! For now I just add this ones I know of (Jug)
807                 case LColor::latex:
808                 case LColor::note:
809                         break;
810                 default:
811                         features.require("color");
812                         lyxerr[Debug::LATEX] << "Color enabled. Font: "
813                                              << fcit->font().stateText(0)
814                                              << endl;
815                 }
816
817                 Language const * language = fcit->font().language();
818                 if (language->babel() != doc_language->babel() &&
819                     language != ignore_language &&
820                     language != latex_language)
821                 {
822                         features.useLanguage(language);
823                         lyxerr[Debug::LATEX] << "Found language "
824                                              << language->babel() << endl;
825                 }
826         }
827
828         if (!params.leftIndent().zero())
829                 features.require("ParagraphLeftIndent");
830
831         // then the insets
832         InsetList::iterator icit = owner_->insetlist.begin();
833         InsetList::iterator iend = owner_->insetlist.end();
834         for (; icit != iend; ++icit) {
835                 if (icit.getInset()) {
836                         icit.getInset()->validate(features);
837                         if (layout.needprotect &&
838                             icit.getInset()->lyxCode() == Inset::FOOT_CODE)
839                                 features.require("NeedLyXFootnoteCode");
840                 }
841         }
842
843         // then the contents
844         for (pos_type i = 0; i < size() ; ++i) {
845                 for (size_t pnr = 0; pnr < phrases_nr; ++pnr) {
846                         if (!special_phrases[pnr].builtin
847                             && isTextAt(special_phrases[pnr].phrase, i)) {
848                                 features.require(special_phrases[pnr].phrase);
849                                 break;
850                         }
851                 }
852         }
853 }
854
855
856 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
857 {
858         InsetList::iterator cit = owner_->insetlist.begin();
859         InsetList::iterator lend = owner_->insetlist.end();
860         Paragraph * result;
861         for (; cit != lend; ++cit) {
862                 if ((result = cit.getInset()->getParFromID(id)))
863                         return result;
864         }
865         return 0;
866 }
867
868
869 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
870                                             BufferParams const & bparams) const
871 {
872         LyXFont tmpfont(font);
873
874         // check for environment font information
875         char par_depth = owner_->getDepth();
876         Paragraph const * par = owner_;
877         LyXTextClass const & tclass = bparams.getLyXTextClass();
878
879         while (par && par->getDepth() && !tmpfont.resolved()) {
880                 par = par->outerHook();
881                 if (par) {
882                         tmpfont.realize(par->layout()->font);
883                         par_depth = par->getDepth();
884                 }
885         }
886
887         tmpfont.realize(tclass.defaultfont());
888         return tmpfont;
889 }