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 // In typewriter mode, we want to avoid
234 // ! . ? : at the end of a line
235 && !(font.family() == LyXFont::TYPEWRITER_FAMILY
236 && (getChar(i - 1) == '.'
237 || getChar(i - 1) == '?'
238 || getChar(i - 1) == ':'
239 || getChar(i - 1) == '!'))) {
240 if (tex_code_break_column == 0) {
241 // in batchmode we need LaTeX to still
242 // see it as a space not as an extra '\n'
248 texrow.start(owner_, i + 1);
250 } else if (style.free_spacing) {
258 bool Paragraph::Pimpl::isTextAt(BufferParams const & bp, LyXFont & font,
259 string const & str, pos_type pos)
261 for (string::size_type i = 0; i < str.length(); ++i) {
262 if (pos + static_cast<pos_type>(i) >= size())
264 if (str[i] != getChar(pos + i))
266 if (owner_->getFont(bp, pos + i) != font)
273 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
274 BufferParams const & bparams,
279 LyXFont & running_font,
282 LyXLayout const & style,
287 if (style.pass_thru) {
288 if (c != '\0') os << c;
291 // Two major modes: LaTeX or plain
292 // Handle here those cases common to both modes
293 // and then split to handle the two modes separately.
295 case Paragraph::META_INSET: {
296 Inset * inset = owner_->getInset(i);
299 int const len = os.tellp();
300 //ostream::pos_type const len = os.tellp();
301 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
302 || inset->lyxCode() == Inset::MATH_CODE
303 || inset->lyxCode() == Inset::URL_CODE)
304 && running_font.isRightToLeft()) {
309 int tmp = inset->latex(buf, os, moving_arg,
316 for (int j = 0; j < tmp; ++j) {
319 texrow.start(owner_, i + 1);
322 column += int(os.tellp()) - len;
328 case Paragraph::META_NEWLINE:
330 column += running_font.latexWriteEndChanges(os,
335 basefont = owner_->getLayoutFont(bparams);
336 running_font = basefont;
339 case Paragraph::META_HFILL:
345 // And now for the special cases within each mode
349 os << "\\textbackslash{}";
353 case '°': case '±': case '²': case '³':
354 case '×': case '÷': case '¹': case 'ª':
355 case 'º': case '¬': case 'µ':
356 if ((bparams.inputenc == "latin1" ||
357 bparams.inputenc == "latin9") ||
358 (bparams.inputenc == "auto" &&
359 (font.language()->encoding()->LatexName()
361 font.language()->encoding()->LatexName()
363 os << "\\ensuremath{"
372 case '|': case '<': case '>':
373 // In T1 encoding, these characters exist
374 if (lyxrc.fontenc == "T1") {
376 //... but we should avoid ligatures
377 if ((c == '>' || c == '<')
379 && getChar(i + 1) == c) {
380 //os << "\\textcompwordmark{}";
381 // Jean-Marc, have a look at
382 // this. I think this works
390 // Typewriter font also has them
391 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
395 // Otherwise, we use what LaTeX
399 os << "\\textless{}";
403 os << "\\textgreater{}";
413 case '-': // "--" in Typewriter mode -> "-{}-"
415 && getChar(i + 1) == '-'
416 && font.family() == LyXFont::TYPEWRITER_FAMILY) {
425 os << "\\char`\\\"{}";
430 if (bparams.inputenc == "default") {
439 case '%': case '#': case '{':
446 os << "\\textasciitilde{}";
451 os << "\\textasciicircum{}";
455 case '*': case '[': case ']':
456 // avoid being mistaken for optional arguments
457 os << '{' << c << '}';
462 // Blanks are printed before font switching.
463 // Sure? I am not! (try nice-latex)
464 // I am sure it's correct. LyX might be smarter
465 // in the future, but for now, nothing wrong is
471 // I assume this is hack treating typewriter as verbatim
472 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
481 // FIXME: if we have "LaTeX" with a font change in the middle (before
482 // the 'T', then the "TeX" part is still special cased. Really we
483 // should only operate this on "words" for some definition of word
487 for (; pnr < phrases_nr; ++pnr) {
488 if (isTextAt(bparams, font, special_phrases[pnr][0], i)) {
489 os << special_phrases[pnr][1];
490 i += special_phrases[pnr][0].length() - 1;
491 column += special_phrases[pnr][1].length() - 1;
496 if (pnr == phrases_nr && c != '\0') {
506 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
507 BufferParams const & bparams,
508 std::ostream & os, TexRow & texrow)
510 lyxerr[Debug::LATEX] << "TeXDeeper... " << this << std::endl;
511 Paragraph * par = owner_;
513 while (par && par->params().depth() == owner_->params().depth()) {
514 if (textclasslist.Style(bparams.textclass,
515 par->layout).isEnvironment()) {
516 par = par->TeXEnvironment(buf, bparams,
519 par = par->TeXOnePar(buf, bparams,
523 lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << std::endl;
529 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
531 InsetList::const_iterator cit = owner_->insetlist.begin();
532 InsetList::const_iterator lend = owner_->insetlist.end();
534 for (; cit != lend; ++cit) {
535 if ((result = cit->inset->getParFromID(id)))
542 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
543 BufferParams const & bparams) const
545 LyXFont tmpfont(font);
547 // check for environment font information
548 char par_depth = owner_->getDepth();
549 Paragraph const * par = owner_;
550 while (par && par->getDepth() && !tmpfont.resolved()) {
551 par = par->outerHook();
553 tmpfont.realize(textclasslist.
554 Style(bparams.textclass,
555 par->getLayout()).font
556 #ifdef INHERIT_LANGUAGE
560 par_depth = par->getDepth();
564 tmpfont.realize(textclasslist.TextClass(bparams.textclass)
566 #ifdef INHERIT_LANGUAGE