1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation
17 #include "paragraph_pimpl.h"
20 #include "bufferparams.h"
24 #include "support/LAssert.h"
28 extern int tex_code_break_column;
31 // Initialize static member.
32 ShareContainer<LyXFont> Paragraph::Pimpl::FontTable::container;
33 // Initialization of the counter for the paragraph id's,
34 unsigned int Paragraph::Pimpl::paragraph_id = 0;
38 string special_phrases[][2] = {
41 { "LaTeX2e", "\\LaTeXe{}" },
42 { "LaTeX", "\\LaTeX{}" },
45 size_t phrases_nr = sizeof(special_phrases)/sizeof(special_phrases[0]);
50 Paragraph::Pimpl::Pimpl(Paragraph * owner)
58 Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner, bool same_ids)
59 : params(p.params), owner_(owner)
61 inset_owner = p.inset_owner;
63 fontlist = p.fontlist;
71 void Paragraph::Pimpl::clear()
77 void Paragraph::Pimpl::setContentsFromPar(Paragraph const * par)
80 text = par->pimpl_->text;
84 Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
86 lyx::Assert(pos <= size());
87 // This is stronger, and I belive that this is the assertion
88 // that we should really use. (Lgb)
89 //Assert(pos < size());
91 // Then this has no meaning. (Lgb)
92 if (!size() || pos == size()) return '\0';
98 void Paragraph::Pimpl::setChar(pos_type pos, value_type c)
104 void Paragraph::Pimpl::insertChar(pos_type pos, value_type c,
105 LyXFont const & font)
107 lyx::Assert(pos <= size());
109 text.insert(text.begin() + pos, c);
111 // Update the font table.
112 FontTable search_font(pos, LyXFont());
113 for (FontList::iterator it = std::lower_bound(fontlist.begin(),
115 search_font, matchFT());
116 it != fontlist.end(); ++it)
118 it->pos(it->pos() + 1);
121 // Update the inset table.
122 InsetTable search_inset(pos, 0);
123 for (InsetList::iterator it = std::lower_bound(owner_->insetlist.begin(),
124 owner_->insetlist.end(),
125 search_inset, matchIT());
126 it != owner_->insetlist.end(); ++it)
130 owner_->setFont(pos, font);
134 void Paragraph::Pimpl::insertInset(pos_type pos,
135 Inset * inset, LyXFont const & font)
138 lyx::Assert(pos <= size());
140 insertChar(pos, META_INSET, font);
141 lyx::Assert(text[pos] == META_INSET);
143 // Add a new entry in the inset table.
144 InsetTable search_inset(pos, 0);
145 InsetList::iterator it = std::lower_bound(owner_->insetlist.begin(),
146 owner_->insetlist.end(),
147 search_inset, matchIT());
148 if (it != owner_->insetlist.end() && it->pos == pos) {
149 lyxerr << "ERROR (Paragraph::InsertInset): "
150 "there is an inset in position: " << pos << std::endl;
152 owner_->insetlist.insert(it, InsetTable(pos, inset));
156 inset->setOwner(inset_owner);
160 void Paragraph::Pimpl::erase(pos_type pos)
162 lyx::Assert(pos < size());
163 // if it is an inset, delete the inset entry
164 if (text[pos] == Paragraph::META_INSET) {
166 InsetTable search_inset(pos, 0);
167 InsetList::iterator it =
168 std::lower_bound(owner_->insetlist.begin(),
169 owner_->insetlist.end(),
170 search_inset, matchIT());
171 if (it != owner_->insetlist.end() && it->pos == pos) {
173 owner_->insetlist.erase(it);
177 text.erase(text.begin() + pos);
179 // Erase entries in the tables.
180 FontTable search_font(pos, LyXFont());
182 FontList::iterator it =
183 std::lower_bound(fontlist.begin(),
185 search_font, matchFT());
186 if (it != fontlist.end() && it->pos() == pos &&
188 (it != fontlist.begin()
189 && boost::prior(it)->pos() == pos - 1))) {
190 // If it is a multi-character font
191 // entry, we just make it smaller
192 // (see update below), otherwise we
194 unsigned int const i = it - fontlist.begin();
195 fontlist.erase(fontlist.begin() + i);
196 it = fontlist.begin() + i;
197 if (i > 0 && i < fontlist.size() &&
198 fontlist[i - 1].font() == fontlist[i].font()) {
199 fontlist.erase(fontlist.begin() + i - 1);
200 it = fontlist.begin() + i - 1;
204 // Update all other entries.
205 FontList::iterator fend = fontlist.end();
206 for (; it != fend; ++it)
207 it->pos(it->pos() - 1);
209 // Update the inset table.
210 InsetTable search_inset(pos, 0);
211 InsetList::iterator lend = owner_->insetlist.end();
212 for (InsetList::iterator it =
213 std::upper_bound(owner_->insetlist.begin(),
215 search_inset, matchIT());
221 void Paragraph::Pimpl::simpleTeXBlanks(std::ostream & os, TexRow & texrow,
223 int & column, LyXFont const & font,
224 LyXLayout const & style)
226 if (style.pass_thru) return;
227 if (column > tex_code_break_column
229 && getChar(i - 1) != ' '
231 // same in FreeSpacing mode
232 && !style.free_spacing
233 && !owner_->isFreeSpacing()
234 // In typewriter mode, we want to avoid
235 // ! . ? : at the end of a line
236 && !(font.family() == LyXFont::TYPEWRITER_FAMILY
237 && (getChar(i - 1) == '.'
238 || getChar(i - 1) == '?'
239 || getChar(i - 1) == ':'
240 || getChar(i - 1) == '!'))) {
241 if (tex_code_break_column == 0) {
242 // in batchmode we need LaTeX to still
243 // see it as a space not as an extra '\n'
249 texrow.start(owner_, i + 1);
251 } else if (style.free_spacing) {
259 bool Paragraph::Pimpl::isTextAt(BufferParams const & bp, LyXFont & font,
260 string const & str, pos_type pos)
262 for (string::size_type i = 0; i < str.length(); ++i) {
263 if (pos + static_cast<pos_type>(i) >= size())
265 if (str[i] != getChar(pos + i))
267 if (owner_->getFont(bp, pos + i) != font)
274 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
275 BufferParams const & bparams,
280 LyXFont & running_font,
283 LyXLayout const & style,
288 if (style.pass_thru) {
289 if (c != '\0') os << c;
292 // Two major modes: LaTeX or plain
293 // Handle here those cases common to both modes
294 // and then split to handle the two modes separately.
296 case Paragraph::META_INSET: {
297 Inset * inset = owner_->getInset(i);
300 int const len = os.tellp();
301 //ostream::pos_type const len = os.tellp();
302 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
303 || inset->lyxCode() == Inset::MATH_CODE
304 || inset->lyxCode() == Inset::URL_CODE)
305 && running_font.isRightToLeft()) {
310 int tmp = inset->latex(buf, os, moving_arg,
317 for (int j = 0; j < tmp; ++j) {
320 texrow.start(owner_, i + 1);
323 column += int(os.tellp()) - len;
329 case Paragraph::META_NEWLINE:
331 column += running_font.latexWriteEndChanges(os,
336 basefont = owner_->getLayoutFont(bparams);
337 running_font = basefont;
340 case Paragraph::META_HFILL:
346 // And now for the special cases within each mode
350 os << "\\textbackslash{}";
354 case '°': case '±': case '²': case '³':
355 case '×': case '÷': case '¹': case 'ª':
356 case 'º': case '¬': case 'µ':
357 if ((bparams.inputenc == "latin1" ||
358 bparams.inputenc == "latin9") ||
359 (bparams.inputenc == "auto" &&
360 (font.language()->encoding()->LatexName()
362 font.language()->encoding()->LatexName()
364 os << "\\ensuremath{"
373 case '|': case '<': case '>':
374 // In T1 encoding, these characters exist
375 if (lyxrc.fontenc == "T1") {
377 //... but we should avoid ligatures
378 if ((c == '>' || c == '<')
380 && getChar(i + 1) == c) {
381 //os << "\\textcompwordmark{}";
382 // Jean-Marc, have a look at
383 // this. I think this works
391 // Typewriter font also has them
392 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
396 // Otherwise, we use what LaTeX
400 os << "\\textless{}";
404 os << "\\textgreater{}";
414 case '-': // "--" in Typewriter mode -> "-{}-"
416 && getChar(i + 1) == '-'
417 && font.family() == LyXFont::TYPEWRITER_FAMILY) {
426 os << "\\char`\\\"{}";
431 if (bparams.inputenc == "default") {
440 case '%': case '#': case '{':
447 os << "\\textasciitilde{}";
452 os << "\\textasciicircum{}";
456 case '*': case '[': case ']':
457 // avoid being mistaken for optional arguments
458 os << '{' << c << '}';
463 // Blanks are printed before font switching.
464 // Sure? I am not! (try nice-latex)
465 // I am sure it's correct. LyX might be smarter
466 // in the future, but for now, nothing wrong is
472 // I assume this is hack treating typewriter as verbatim
473 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
482 // FIXME: if we have "LaTeX" with a font change in the middle (before
483 // the 'T', then the "TeX" part is still special cased. Really we
484 // should only operate this on "words" for some definition of word
488 for (; pnr < phrases_nr; ++pnr) {
489 if (isTextAt(bparams, font, special_phrases[pnr][0], i)) {
490 os << special_phrases[pnr][1];
491 i += special_phrases[pnr][0].length() - 1;
492 column += special_phrases[pnr][1].length() - 1;
497 if (pnr == phrases_nr && c != '\0') {
507 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
508 BufferParams const & bparams,
509 std::ostream & os, TexRow & texrow)
511 lyxerr[Debug::LATEX] << "TeXDeeper... " << this << std::endl;
512 Paragraph * par = owner_;
514 while (par && par->params().depth() == owner_->params().depth()) {
515 if (textclasslist.Style(bparams.textclass,
516 par->layout).isEnvironment()) {
517 par = par->TeXEnvironment(buf, bparams,
520 par = par->TeXOnePar(buf, bparams,
524 lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << std::endl;
530 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
532 InsetList::const_iterator cit = owner_->insetlist.begin();
533 InsetList::const_iterator lend = owner_->insetlist.end();
535 for (; cit != lend; ++cit) {
536 if ((result = cit->inset->getParFromID(id)))
543 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
544 BufferParams const & bparams) const
546 LyXFont tmpfont(font);
548 // check for environment font information
549 char par_depth = owner_->getDepth();
550 Paragraph const * par = owner_;
551 while (par && par->getDepth() && !tmpfont.resolved()) {
552 par = par->outerHook();
554 tmpfont.realize(textclasslist.
555 Style(bparams.textclass,
556 par->getLayout()).font
557 #ifdef INHERIT_LANGUAGE
561 par_depth = par->getDepth();
565 tmpfont.realize(textclasslist.TextClass(bparams.textclass)
567 #ifdef INHERIT_LANGUAGE