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));
153 inset->parOwner(owner_);
157 inset->setOwner(inset_owner);
161 void Paragraph::Pimpl::erase(pos_type pos)
163 lyx::Assert(pos < size());
164 // if it is an inset, delete the inset entry
165 if (text[pos] == Paragraph::META_INSET) {
167 InsetTable search_inset(pos, 0);
168 InsetList::iterator it =
169 std::lower_bound(owner_->insetlist.begin(),
170 owner_->insetlist.end(),
171 search_inset, matchIT());
172 if (it != owner_->insetlist.end() && it->pos == pos) {
174 owner_->insetlist.erase(it);
178 text.erase(text.begin() + pos);
180 // Erase entries in the tables.
181 FontTable search_font(pos, LyXFont());
183 FontList::iterator it =
184 std::lower_bound(fontlist.begin(),
186 search_font, matchFT());
187 if (it != fontlist.end() && it->pos() == pos &&
189 (it != fontlist.begin()
190 && boost::prior(it)->pos() == pos - 1))) {
191 // If it is a multi-character font
192 // entry, we just make it smaller
193 // (see update below), otherwise we
195 unsigned int const i = it - fontlist.begin();
196 fontlist.erase(fontlist.begin() + i);
197 it = fontlist.begin() + i;
198 if (i > 0 && i < fontlist.size() &&
199 fontlist[i - 1].font() == fontlist[i].font()) {
200 fontlist.erase(fontlist.begin() + i - 1);
201 it = fontlist.begin() + i - 1;
205 // Update all other entries.
206 FontList::iterator fend = fontlist.end();
207 for (; it != fend; ++it)
208 it->pos(it->pos() - 1);
210 // Update the inset table.
211 InsetTable search_inset(pos, 0);
212 InsetList::iterator lend = owner_->insetlist.end();
213 for (InsetList::iterator it =
214 std::upper_bound(owner_->insetlist.begin(),
216 search_inset, matchIT());
222 void Paragraph::Pimpl::simpleTeXBlanks(std::ostream & os, TexRow & texrow,
224 int & column, LyXFont const & font,
225 LyXLayout const & style)
227 if (style.pass_thru) return;
228 if (column > tex_code_break_column
230 && getChar(i - 1) != ' '
232 // same in FreeSpacing mode
233 && !style.free_spacing
234 && !owner_->isFreeSpacing()
235 // In typewriter mode, we want to avoid
236 // ! . ? : at the end of a line
237 && !(font.family() == LyXFont::TYPEWRITER_FAMILY
238 && (getChar(i - 1) == '.'
239 || getChar(i - 1) == '?'
240 || getChar(i - 1) == ':'
241 || getChar(i - 1) == '!'))) {
242 if (tex_code_break_column == 0) {
243 // in batchmode we need LaTeX to still
244 // see it as a space not as an extra '\n'
250 texrow.start(owner_, i + 1);
252 } else if (style.free_spacing) {
260 bool Paragraph::Pimpl::isTextAt(BufferParams const & bp,
261 string const & str, pos_type pos)
263 LyXFont const & font = owner_->getFont(bp, pos);
265 for (string::size_type i = 0; i < str.length(); ++i) {
266 if (pos + static_cast<pos_type>(i) >= size())
268 if (str[i] != getChar(pos + i))
270 if (owner_->getFont(bp, pos + i) != font)
277 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
278 BufferParams const & bparams,
283 LyXFont & running_font,
286 LyXLayout const & style,
291 if (style.pass_thru) {
292 if (c != '\0') os << c;
295 // Two major modes: LaTeX or plain
296 // Handle here those cases common to both modes
297 // and then split to handle the two modes separately.
299 case Paragraph::META_INSET: {
300 Inset * inset = owner_->getInset(i);
303 int const len = os.tellp();
304 //ostream::pos_type const len = os.tellp();
305 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
306 || inset->lyxCode() == Inset::MATH_CODE
307 || inset->lyxCode() == Inset::URL_CODE)
308 && running_font.isRightToLeft()) {
313 int tmp = inset->latex(buf, os, moving_arg,
320 for (int j = 0; j < tmp; ++j) {
323 texrow.start(owner_, i + 1);
326 column += int(os.tellp()) - len;
332 case Paragraph::META_NEWLINE:
334 column += running_font.latexWriteEndChanges(os,
339 basefont = owner_->getLayoutFont(bparams);
340 running_font = basefont;
343 case Paragraph::META_HFILL:
349 // And now for the special cases within each mode
353 os << "\\textbackslash{}";
357 case '°': case '±': case '²': case '³':
358 case '×': case '÷': case '¹': case 'ª':
359 case 'º': case '¬': case 'µ':
360 if ((bparams.inputenc == "latin1" ||
361 bparams.inputenc == "latin9") ||
362 (bparams.inputenc == "auto" &&
363 (font.language()->encoding()->LatexName()
365 font.language()->encoding()->LatexName()
367 os << "\\ensuremath{"
376 case '|': case '<': case '>':
377 // In T1 encoding, these characters exist
378 if (lyxrc.fontenc == "T1") {
380 //... but we should avoid ligatures
381 if ((c == '>' || c == '<')
383 && getChar(i + 1) == c) {
384 //os << "\\textcompwordmark{}";
385 // Jean-Marc, have a look at
386 // this. I think this works
394 // Typewriter font also has them
395 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
399 // Otherwise, we use what LaTeX
403 os << "\\textless{}";
407 os << "\\textgreater{}";
417 case '-': // "--" in Typewriter mode -> "-{}-"
419 && getChar(i + 1) == '-'
420 && font.family() == LyXFont::TYPEWRITER_FAMILY) {
429 os << "\\char`\\\"{}";
434 if (bparams.inputenc == "default") {
443 case '%': case '#': case '{':
450 os << "\\textasciitilde{}";
455 os << "\\textasciicircum{}";
459 case '*': case '[': case ']':
460 // avoid being mistaken for optional arguments
461 os << '{' << c << '}';
466 // Blanks are printed before font switching.
467 // Sure? I am not! (try nice-latex)
468 // I am sure it's correct. LyX might be smarter
469 // in the future, but for now, nothing wrong is
475 // I assume this is hack treating typewriter as verbatim
476 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
485 // FIXME: if we have "LaTeX" with a font change in the middle (before
486 // the 'T', then the "TeX" part is still special cased. Really we
487 // should only operate this on "words" for some definition of word
491 for (; pnr < phrases_nr; ++pnr) {
492 if (isTextAt(bparams, special_phrases[pnr][0], i)) {
493 os << special_phrases[pnr][1];
494 i += special_phrases[pnr][0].length() - 1;
495 column += special_phrases[pnr][1].length() - 1;
500 if (pnr == phrases_nr && c != '\0') {
510 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
511 BufferParams const & bparams,
512 std::ostream & os, TexRow & texrow)
514 lyxerr[Debug::LATEX] << "TeXDeeper... " << this << std::endl;
515 Paragraph * par = owner_;
517 while (par && par->params().depth() == owner_->params().depth()) {
518 if (textclasslist.Style(bparams.textclass,
519 par->layout).isEnvironment()) {
520 par = par->TeXEnvironment(buf, bparams,
523 par = par->TeXOnePar(buf, bparams,
527 lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << std::endl;
533 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
535 InsetList::const_iterator cit = owner_->insetlist.begin();
536 InsetList::const_iterator lend = owner_->insetlist.end();
538 for (; cit != lend; ++cit) {
539 if ((result = cit->inset->getParFromID(id)))
546 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
547 BufferParams const & bparams) const
549 LyXFont tmpfont(font);
551 // check for environment font information
552 char par_depth = owner_->getDepth();
553 Paragraph const * par = owner_;
554 while (par && par->getDepth() && !tmpfont.resolved()) {
555 par = par->outerHook();
557 tmpfont.realize(textclasslist.
558 Style(bparams.textclass,
559 par->getLayout()).font
560 #ifdef INHERIT_LANGUAGE
564 par_depth = par->getDepth();
568 tmpfont.realize(textclasslist.TextClass(bparams.textclass)
570 #ifdef INHERIT_LANGUAGE