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,
260 string const & str, pos_type pos)
262 LyXFont const & font = owner_->getFont(bp, pos);
264 for (string::size_type i = 0; i < str.length(); ++i) {
265 if (pos + static_cast<pos_type>(i) >= size())
267 if (str[i] != getChar(pos + i))
269 if (owner_->getFont(bp, pos + i) != font)
276 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
277 BufferParams const & bparams,
282 LyXFont & running_font,
285 LyXLayout const & style,
290 if (style.pass_thru) {
291 if (c != '\0') os << c;
294 // Two major modes: LaTeX or plain
295 // Handle here those cases common to both modes
296 // and then split to handle the two modes separately.
298 case Paragraph::META_INSET: {
299 Inset * inset = owner_->getInset(i);
302 int const len = os.tellp();
303 //ostream::pos_type const len = os.tellp();
304 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
305 || inset->lyxCode() == Inset::MATH_CODE
306 || inset->lyxCode() == Inset::URL_CODE)
307 && running_font.isRightToLeft()) {
312 int tmp = inset->latex(buf, os, moving_arg,
319 for (int j = 0; j < tmp; ++j) {
322 texrow.start(owner_, i + 1);
325 column += int(os.tellp()) - len;
331 case Paragraph::META_NEWLINE:
333 column += running_font.latexWriteEndChanges(os,
338 basefont = owner_->getLayoutFont(bparams);
339 running_font = basefont;
342 case Paragraph::META_HFILL:
348 // And now for the special cases within each mode
352 os << "\\textbackslash{}";
356 case '°': case '±': case '²': case '³':
357 case '×': case '÷': case '¹': case 'ª':
358 case 'º': case '¬': case 'µ':
359 if ((bparams.inputenc == "latin1" ||
360 bparams.inputenc == "latin9") ||
361 (bparams.inputenc == "auto" &&
362 (font.language()->encoding()->LatexName()
364 font.language()->encoding()->LatexName()
366 os << "\\ensuremath{"
375 case '|': case '<': case '>':
376 // In T1 encoding, these characters exist
377 if (lyxrc.fontenc == "T1") {
379 //... but we should avoid ligatures
380 if ((c == '>' || c == '<')
382 && getChar(i + 1) == c) {
383 //os << "\\textcompwordmark{}";
384 // Jean-Marc, have a look at
385 // this. I think this works
393 // Typewriter font also has them
394 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
398 // Otherwise, we use what LaTeX
402 os << "\\textless{}";
406 os << "\\textgreater{}";
416 case '-': // "--" in Typewriter mode -> "-{}-"
418 && getChar(i + 1) == '-'
419 && font.family() == LyXFont::TYPEWRITER_FAMILY) {
428 os << "\\char`\\\"{}";
433 if (bparams.inputenc == "default") {
442 case '%': case '#': case '{':
449 os << "\\textasciitilde{}";
454 os << "\\textasciicircum{}";
458 case '*': case '[': case ']':
459 // avoid being mistaken for optional arguments
460 os << '{' << c << '}';
465 // Blanks are printed before font switching.
466 // Sure? I am not! (try nice-latex)
467 // I am sure it's correct. LyX might be smarter
468 // in the future, but for now, nothing wrong is
474 // I assume this is hack treating typewriter as verbatim
475 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
484 // FIXME: if we have "LaTeX" with a font change in the middle (before
485 // the 'T', then the "TeX" part is still special cased. Really we
486 // should only operate this on "words" for some definition of word
490 for (; pnr < phrases_nr; ++pnr) {
491 if (isTextAt(bparams, special_phrases[pnr][0], i)) {
492 os << special_phrases[pnr][1];
493 i += special_phrases[pnr][0].length() - 1;
494 column += special_phrases[pnr][1].length() - 1;
499 if (pnr == phrases_nr && c != '\0') {
509 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
510 BufferParams const & bparams,
511 std::ostream & os, TexRow & texrow)
513 lyxerr[Debug::LATEX] << "TeXDeeper... " << this << std::endl;
514 Paragraph * par = owner_;
516 while (par && par->params().depth() == owner_->params().depth()) {
517 if (textclasslist.Style(bparams.textclass,
518 par->layout).isEnvironment()) {
519 par = par->TeXEnvironment(buf, bparams,
522 par = par->TeXOnePar(buf, bparams,
526 lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << std::endl;
532 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
534 InsetList::const_iterator cit = owner_->insetlist.begin();
535 InsetList::const_iterator lend = owner_->insetlist.end();
537 for (; cit != lend; ++cit) {
538 if ((result = cit->inset->getParFromID(id)))
545 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
546 BufferParams const & bparams) const
548 LyXFont tmpfont(font);
550 // check for environment font information
551 char par_depth = owner_->getDepth();
552 Paragraph const * par = owner_;
553 while (par && par->getDepth() && !tmpfont.resolved()) {
554 par = par->outerHook();
556 tmpfont.realize(textclasslist.
557 Style(bparams.textclass,
558 par->getLayout()).font
559 #ifdef INHERIT_LANGUAGE
563 par_depth = par->getDepth();
567 tmpfont.realize(textclasslist.TextClass(bparams.textclass)
569 #ifdef INHERIT_LANGUAGE