]> git.lyx.org Git - lyx.git/blob - src/paragraph_pimpl.C
layout as string
[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 "texrow.h"
19 #include "language.h"
20 #include "bufferparams.h"
21 #include "encoding.h"
22 #include "lyxrc.h"
23 #include "debug.h"
24 #include "lyxtextclasslist.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 extern int tex_code_break_column;
35
36
37 // Initialize static member.
38 ShareContainer<LyXFont> Paragraph::Pimpl::FontTable::container;
39 // Initialization of the counter for the paragraph id's,
40 unsigned int Paragraph::Pimpl::paragraph_id = 0;
41
42 namespace {
43  
44 string special_phrases[][2] = {
45         { "LyX", "\\LyX{}" },
46         { "TeX", "\\TeX{}" },
47         { "LaTeX2e", "\\LaTeXe{}" },
48         { "LaTeX", "\\LaTeX{}" },
49         };
50  
51 size_t phrases_nr = sizeof(special_phrases)/sizeof(special_phrases[0]);
52  
53 } // namespace anon
54  
55
56 Paragraph::Pimpl::Pimpl(Paragraph * owner)
57         : owner_(owner)
58 {
59         inset_owner = 0;
60         id_ = paragraph_id++;
61 }
62
63
64 Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner, bool same_ids)
65         : params(p.params), owner_(owner)
66 {
67         inset_owner = p.inset_owner;
68         text = p.text;
69         fontlist = p.fontlist;
70         if (same_ids)
71                 id_ = p.id_;
72         else
73                 id_ = paragraph_id++;
74 }
75
76
77 void Paragraph::Pimpl::clear()
78 {
79         text.clear();
80 }
81
82
83 void Paragraph::Pimpl::setContentsFromPar(Paragraph const * par)
84 {
85         lyx::Assert(par);
86         text = par->pimpl_->text;
87 }
88
89
90 Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
91 {
92         lyx::Assert(pos <= size());
93         // This is stronger, and I belive that this is the assertion
94         // that we should really use. (Lgb)
95         //Assert(pos < size());
96
97         // Then this has no meaning. (Lgb)
98         if (!size() || pos == size()) return '\0';
99         
100         return text[pos];
101 }
102
103
104 void Paragraph::Pimpl::setChar(pos_type pos, value_type c)
105 {
106         text[pos] = c;
107 }
108
109
110 void Paragraph::Pimpl::insertChar(pos_type pos, value_type c,
111                                   LyXFont const & font)
112 {
113         lyx::Assert(pos <= size());
114
115         text.insert(text.begin() + pos, c);
116
117         // Update the font table.
118         FontTable search_font(pos, LyXFont());
119         for (FontList::iterator it = lower_bound(fontlist.begin(),
120                                                       fontlist.end(),
121                                                       search_font, matchFT());
122              it != fontlist.end(); ++it)
123         {
124                 it->pos(it->pos() + 1);
125         }
126    
127         // Update the inset table.
128         InsetTable search_inset(pos, 0);
129         for (InsetList::iterator it = lower_bound(owner_->insetlist.begin(),
130                                                        owner_->insetlist.end(),
131                                                        search_inset, matchIT());
132              it != owner_->insetlist.end(); ++it)
133         {
134                 ++it->pos;
135         }
136         owner_->setFont(pos, font);
137 }
138
139
140 void Paragraph::Pimpl::insertInset(pos_type pos,
141                                    Inset * inset, LyXFont const & font)
142 {
143         lyx::Assert(inset);
144         lyx::Assert(pos <= size());
145         
146         insertChar(pos, META_INSET, font);
147         lyx::Assert(text[pos] == META_INSET);
148         
149         // Add a new entry in the inset table.
150         InsetTable search_inset(pos, 0);
151         InsetList::iterator it = lower_bound(owner_->insetlist.begin(),
152                                                   owner_->insetlist.end(),
153                                                   search_inset, matchIT());
154         if (it != owner_->insetlist.end() && it->pos == pos) {
155                 lyxerr << "ERROR (Paragraph::InsertInset): "
156                         "there is an inset in position: " << pos << endl;
157         } else {
158                 owner_->insetlist.insert(it, InsetTable(pos, inset));
159                 inset->parOwner(owner_);
160         }
161         
162         if (inset_owner)
163                 inset->setOwner(inset_owner);
164 }
165
166
167 void Paragraph::Pimpl::erase(pos_type pos)
168 {
169         lyx::Assert(pos < size());
170         // if it is an inset, delete the inset entry 
171         if (text[pos] == Paragraph::META_INSET) {
172                 // find the entry
173                 InsetTable search_inset(pos, 0);
174                 InsetList::iterator it =
175                         lower_bound(owner_->insetlist.begin(),
176                                          owner_->insetlist.end(),
177                                          search_inset, matchIT());
178                 if (it != owner_->insetlist.end() && it->pos == pos) {
179                         delete it->inset;
180                         owner_->insetlist.erase(it);
181                 }
182         }
183         
184         text.erase(text.begin() + pos);
185         
186         // Erase entries in the tables.
187         FontTable search_font(pos, LyXFont());
188         
189         FontList::iterator it =
190                 lower_bound(fontlist.begin(),
191                             fontlist.end(),
192                             search_font, matchFT());
193         if (it != fontlist.end() && it->pos() == pos &&
194             (pos == 0 || 
195              (it != fontlist.begin() 
196               && boost::prior(it)->pos() == pos - 1))) {
197                 // If it is a multi-character font
198                 // entry, we just make it smaller
199                 // (see update below), otherwise we
200                 // should delete it.
201                 unsigned int const i = it - fontlist.begin();
202                 fontlist.erase(fontlist.begin() + i);
203                 it = fontlist.begin() + i;
204                 if (i > 0 && i < fontlist.size() &&
205                     fontlist[i - 1].font() == fontlist[i].font()) {
206                         fontlist.erase(fontlist.begin() + i - 1);
207                         it = fontlist.begin() + i - 1;
208                 }
209         }
210         
211         // Update all other entries.
212         FontList::iterator fend = fontlist.end();
213         for (; it != fend; ++it)
214                 it->pos(it->pos() - 1);
215         
216         // Update the inset table.
217         InsetTable search_inset(pos, 0);
218         InsetList::iterator lend = owner_->insetlist.end();
219         for (InsetList::iterator it =
220                      upper_bound(owner_->insetlist.begin(),
221                                       lend,
222                                       search_inset, matchIT());
223              it != lend; ++it)
224                 --it->pos;
225 }
226
227
228 void Paragraph::Pimpl::simpleTeXBlanks(ostream & os, TexRow & texrow,
229                                        pos_type const i,
230                                        int & column, LyXFont const & font,
231                                        LyXLayout const & style)
232 {
233         if (style.pass_thru) return;
234         if (column > tex_code_break_column
235             && i 
236             && getChar(i - 1) != ' '
237             && (i < size() - 1)
238             // same in FreeSpacing mode
239             && !style.free_spacing
240                 && !owner_->isFreeSpacing()
241             // In typewriter mode, we want to avoid 
242             // ! . ? : at the end of a line
243             && !(font.family() == LyXFont::TYPEWRITER_FAMILY
244                  && (getChar(i - 1) == '.'
245                      || getChar(i - 1) == '?' 
246                      || getChar(i - 1) == ':'
247                      || getChar(i - 1) == '!'))) {
248                 if (tex_code_break_column == 0) {
249                         // in batchmode we need LaTeX to still
250                         // see it as a space not as an extra '\n'
251                         os << " %\n";
252                 } else {
253                         os << '\n';
254                 }
255                 texrow.newline();
256                 texrow.start(owner_, i + 1);
257                 column = 0;
258         } else if (style.free_spacing) {
259                 os << '~';
260         } else {
261                 os << ' ';
262         }
263 }
264
265
266 bool Paragraph::Pimpl::isTextAt(BufferParams const & bp,
267                                 string const & str, pos_type pos)
268 {
269         LyXFont const & font = owner_->getFont(bp, pos);
270  
271         for (string::size_type i = 0; i < str.length(); ++i) {
272                 if (pos + static_cast<pos_type>(i) >= size())
273                         return false;
274                 if (str[i] != getChar(pos + i))
275                         return false;
276                 if (owner_->getFont(bp, pos + i) != font)
277                         return false;
278         }
279         return true;
280 }
281
282  
283 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
284                                              BufferParams const & bparams,
285                                              ostream & os,
286                                              TexRow & texrow,
287                                              bool moving_arg,
288                                              LyXFont & font,
289                                              LyXFont & running_font,
290                                              LyXFont & basefont,
291                                              bool & open_font,
292                                              LyXLayout const & style,
293                                              pos_type & i,
294                                              int & column,
295                                              value_type const c)
296 {
297         if (style.pass_thru) {
298                 if (c != '\0') os << c;
299                 return;
300         }
301         // Two major modes:  LaTeX or plain
302         // Handle here those cases common to both modes
303         // and then split to handle the two modes separately.
304         switch (c) {
305         case Paragraph::META_INSET: {
306                 Inset * inset = owner_->getInset(i);
307                 if (inset) {
308                         bool close = false;
309                         int const len = os.tellp();
310                         //ostream::pos_type const len = os.tellp();
311                         if ((inset->lyxCode() == Inset::GRAPHICS_CODE
312                              || inset->lyxCode() == Inset::MATH_CODE
313                              || inset->lyxCode() == Inset::URL_CODE)
314                             && running_font.isRightToLeft()) {
315                                 os << "\\L{";
316                                 close = true;
317                         }
318
319                         int tmp = inset->latex(buf, os, moving_arg,
320                                                style.free_spacing);
321
322                         if (close)
323                                 os << "}";
324
325                         if (tmp) {
326                                 for (int j = 0; j < tmp; ++j) {
327                                         texrow.newline();
328                                 }
329                                 texrow.start(owner_, i + 1);
330                                 column = 0;
331                         } else {
332                                 column += int(os.tellp()) - len;
333                         }
334                 }
335         }
336         break;
337
338         case Paragraph::META_NEWLINE:
339                 if (open_font) {
340                         column += running_font.latexWriteEndChanges(os,
341                                                                     basefont,
342                                                                     basefont);
343                         open_font = false;
344                 }
345                 basefont = owner_->getLayoutFont(bparams);
346                 running_font = basefont;
347                 break;
348
349         case Paragraph::META_HFILL: 
350                 os << "\\hfill{}";
351                 column += 7;
352                 break;
353
354         default:
355                 // And now for the special cases within each mode
356
357                 switch (c) {
358                 case '\\': 
359                         os << "\\textbackslash{}";
360                         column += 15;
361                         break;
362                         
363                 case '°': case '±': case '²': case '³':  
364                 case '×': case '÷': case '¹': case 'ª':
365                 case 'º': case '¬': case 'µ':
366                         if ((bparams.inputenc == "latin1" ||
367                              bparams.inputenc == "latin9") ||
368                             (bparams.inputenc == "auto" &&
369                              (font.language()->encoding()->LatexName()
370                               == "latin1" ||
371                               font.language()->encoding()->LatexName()
372                               == "latin9"))) {
373                                 os << "\\ensuremath{"
374                                    << c
375                                    << '}';
376                                 column += 13;
377                         } else {
378                                 os << c;
379                         }
380                         break;
381                         
382                 case '|': case '<': case '>':
383                         // In T1 encoding, these characters exist
384                         if (lyxrc.fontenc == "T1") {
385                                 os << c;
386                                 //... but we should avoid ligatures
387                                 if ((c == '>' || c == '<')
388                                     && i <= size() - 2
389                                     && getChar(i + 1) == c) {
390                                         //os << "\\textcompwordmark{}";
391                                         // Jean-Marc, have a look at
392                                         // this. I think this works
393                                         // equally well:
394                                         os << "\\,{}";
395                                         // Lgb
396                                         column += 19;
397                                 }
398                                 break;
399                         }
400                         // Typewriter font also has them
401                         if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
402                                 os << c;
403                                 break;
404                         } 
405                         // Otherwise, we use what LaTeX
406                         // provides us.
407                         switch (c) {
408                         case '<':
409                                 os << "\\textless{}";
410                                 column += 10;
411                                 break;
412                         case '>':
413                                 os << "\\textgreater{}";
414                                 column += 13;
415                                 break;
416                         case '|':
417                                 os << "\\textbar{}";
418                                 column += 9;
419                                 break;
420                         }
421                         break;
422                         
423                 case '-': // "--" in Typewriter mode -> "-{}-"
424                         if (i <= size() - 2
425                             && getChar(i + 1) == '-'
426                             && font.family() == LyXFont::TYPEWRITER_FAMILY) {
427                                 os << "-{}";
428                                 column += 2;
429                         } else {
430                                 os << '-';
431                         }
432                         break;
433                         
434                 case '\"': 
435                         os << "\\char`\\\"{}";
436                         column += 9;
437                         break;
438                         
439                 case '£':
440                         if (bparams.inputenc == "default") {
441                                 os << "\\pounds{}";
442                                 column += 8;
443                         } else {
444                                 os << c;
445                         }
446                         break;
447                         
448                 case '$': case '&':
449                 case '%': case '#': case '{':
450                 case '}': case '_':
451                         os << '\\' << c;
452                         column += 1;
453                         break;
454                         
455                 case '~':
456                         os << "\\textasciitilde{}";
457                         column += 16;
458                         break;
459                         
460                 case '^':
461                         os << "\\textasciicircum{}";
462                         column += 17;
463                         break;
464                         
465                 case '*': case '[': case ']':
466                         // avoid being mistaken for optional arguments
467                         os << '{' << c << '}';
468                         column += 2;
469                         break;
470
471                 case ' ':
472                         // Blanks are printed before font switching.
473                         // Sure? I am not! (try nice-latex)
474                         // I am sure it's correct. LyX might be smarter
475                         // in the future, but for now, nothing wrong is
476                         // written. (Asger)
477                         break;
478
479                 default:
480  
481                         // I assume this is hack treating typewriter as verbatim
482                         if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
483                                 if (c != '\0') {
484                                         os << c;
485                                 }
486                                 break;
487                         }
488                 
489                         // LyX, LaTeX etc.
490  
491                         // FIXME: if we have "LaTeX" with a font change in the middle (before
492                         // the 'T', then the "TeX" part is still special cased. Really we
493                         // should only operate this on "words" for some definition of word
494  
495                         size_t pnr = 0;
496  
497                         for (; pnr < phrases_nr; ++pnr) {
498                                 if (isTextAt(bparams, special_phrases[pnr][0], i)) {
499                                         os << special_phrases[pnr][1];
500                                         i += special_phrases[pnr][0].length() - 1;
501                                         column += special_phrases[pnr][1].length() - 1;
502                                         break;
503                                 }
504                         }
505
506                         if (pnr == phrases_nr && c != '\0') {
507                                 os << c;
508                         }
509                         break;
510                 }
511         }
512 }
513
514
515
516 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
517                                         BufferParams const & bparams,
518                                         ostream & os, TexRow & texrow)
519 {
520         lyxerr[Debug::LATEX] << "TeXDeeper...     " << this << endl;
521         Paragraph * par = owner_;
522
523         while (par && par->params().depth() == owner_->params().depth()) {
524                 if (textclasslist[bparams.textclass][par->layout()].isEnvironment()) {
525                         par = par->TeXEnvironment(buf, bparams,
526                                                   os, texrow);
527                 } else {
528                         par = par->TeXOnePar(buf, bparams,
529                                              os, texrow, false);
530                 }
531         }
532         lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << endl;
533
534         return par;
535 }
536
537
538 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
539 {
540         InsetList::const_iterator cit = owner_->insetlist.begin();
541         InsetList::const_iterator lend = owner_->insetlist.end();
542         Paragraph * result;
543         for (; cit != lend; ++cit) {
544                 if ((result = cit->inset->getParFromID(id)))
545                         return result;
546         }
547         return 0;
548 }
549
550
551 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
552                                             BufferParams const & bparams) const
553 {
554         LyXFont tmpfont(font);
555         
556         // check for environment font information
557         char par_depth = owner_->getDepth();
558         Paragraph const * par = owner_;
559         LyXTextClass const & tclass = textclasslist[bparams.textclass];
560         
561         while (par && par->getDepth() && !tmpfont.resolved()) {
562                 par = par->outerHook();
563                 if (par) {
564                         tmpfont.realize(tclass[par->layout()].font
565 #ifdef INHERIT_LANGUAGE
566                                         , bparams.language
567 #endif
568                                         );
569                         par_depth = par->getDepth();
570                 }
571         }
572
573         tmpfont.realize(tclass.defaultfont()
574 #ifdef INHERIT_LANGUAGE
575                 , bparams.language
576 #endif
577                 );
578         return tmpfont; 
579 }