]> git.lyx.org Git - lyx.git/blob - src/paragraph_pimpl.C
small cleanup
[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 "support/LAssert.h"
25
26 using lyx::pos_type;
27
28 extern int tex_code_break_column;
29
30
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;
35
36 namespace {
37  
38 string special_phrases[][2] = {
39         { "LyX", "\\LyX{}" },
40         { "TeX", "\\TeX{}" },
41         { "LaTeX2e", "\\LaTeXe{}" },
42         { "LaTeX", "\\LaTeX{}" },
43         };
44  
45 size_t phrases_nr = sizeof(special_phrases)/sizeof(special_phrases[0]);
46  
47 } // namespace anon
48  
49
50 Paragraph::Pimpl::Pimpl(Paragraph * owner)
51         : owner_(owner)
52 {
53         inset_owner = 0;
54         id_ = paragraph_id++;
55 }
56
57
58 Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner, bool same_ids)
59         : params(p.params), owner_(owner)
60 {
61         inset_owner = p.inset_owner;
62         text = p.text;
63         fontlist = p.fontlist;
64         if (same_ids)
65                 id_ = p.id_;
66         else
67                 id_ = paragraph_id++;
68 }
69
70
71 void Paragraph::Pimpl::clear()
72 {
73         text.clear();
74 }
75
76
77 void Paragraph::Pimpl::setContentsFromPar(Paragraph const * par)
78 {
79         lyx::Assert(par);
80         text = par->pimpl_->text;
81 }
82
83
84 Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
85 {
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());
90
91         // Then this has no meaning. (Lgb)
92         if (!size() || pos == size()) return '\0';
93         
94         return text[pos];
95 }
96
97
98 void Paragraph::Pimpl::setChar(pos_type pos, value_type c)
99 {
100         text[pos] = c;
101 }
102
103
104 void Paragraph::Pimpl::insertChar(pos_type pos, value_type c,
105                                   LyXFont const & font)
106 {
107         lyx::Assert(pos <= size());
108
109         text.insert(text.begin() + pos, c);
110
111         // Update the font table.
112         FontTable search_font(pos, LyXFont());
113         for (FontList::iterator it = std::lower_bound(fontlist.begin(),
114                                                       fontlist.end(),
115                                                       search_font, matchFT());
116              it != fontlist.end(); ++it)
117         {
118                 it->pos(it->pos() + 1);
119         }
120    
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)
127         {
128                 ++it->pos;
129         }
130         owner_->setFont(pos, font);
131 }
132
133
134 void Paragraph::Pimpl::insertInset(pos_type pos,
135                                    Inset * inset, LyXFont const & font)
136 {
137         lyx::Assert(inset);
138         lyx::Assert(pos <= size());
139         
140         insertChar(pos, META_INSET, font);
141         lyx::Assert(text[pos] == META_INSET);
142         
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;
151         } else {
152                 owner_->insetlist.insert(it, InsetTable(pos, inset));
153         }
154         
155         if (inset_owner)
156                 inset->setOwner(inset_owner);
157 }
158
159
160 void Paragraph::Pimpl::erase(pos_type pos)
161 {
162         lyx::Assert(pos < size());
163         // if it is an inset, delete the inset entry 
164         if (text[pos] == Paragraph::META_INSET) {
165                 // find the entry
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) {
172                         delete it->inset;
173                         owner_->insetlist.erase(it);
174                 }
175         }
176         
177         text.erase(text.begin() + pos);
178         
179         // Erase entries in the tables.
180         FontTable search_font(pos, LyXFont());
181         
182         FontList::iterator it =
183                 std::lower_bound(fontlist.begin(),
184                             fontlist.end(),
185                             search_font, matchFT());
186         if (it != fontlist.end() && it->pos() == pos &&
187             (pos == 0 || 
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
193                 // should delete it.
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;
201                 }
202         }
203         
204         // Update all other entries.
205         FontList::iterator fend = fontlist.end();
206         for (; it != fend; ++it)
207                 it->pos(it->pos() - 1);
208         
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(),
214                                       lend,
215                                       search_inset, matchIT());
216              it != lend; ++it)
217                 --it->pos;
218 }
219
220
221 void Paragraph::Pimpl::simpleTeXBlanks(std::ostream & os, TexRow & texrow,
222                                        pos_type const i,
223                                        int & column, LyXFont const & font,
224                                        LyXLayout const & style)
225 {
226         if (style.pass_thru) return;
227         if (column > tex_code_break_column
228             && i 
229             && getChar(i - 1) != ' '
230             && (i < size() - 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'
244                         os << " %\n";
245                 } else {
246                         os << '\n';
247                 }
248                 texrow.newline();
249                 texrow.start(owner_, i + 1);
250                 column = 0;
251         } else if (style.free_spacing) {
252                 os << '~';
253         } else {
254                 os << ' ';
255         }
256 }
257
258
259 bool Paragraph::Pimpl::isTextAt(BufferParams const & bp,
260                                 string const & str, pos_type pos)
261 {
262         LyXFont const & font = owner_->getFont(bp, pos);
263  
264         for (string::size_type i = 0; i < str.length(); ++i) {
265                 if (pos + static_cast<pos_type>(i) >= size())
266                         return false;
267                 if (str[i] != getChar(pos + i))
268                         return false;
269                 if (owner_->getFont(bp, pos + i) != font)
270                         return false;
271         }
272         return true;
273 }
274
275  
276 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
277                                              BufferParams const & bparams,
278                                              std::ostream & os,
279                                              TexRow & texrow,
280                                              bool moving_arg,
281                                              LyXFont & font,
282                                              LyXFont & running_font,
283                                              LyXFont & basefont,
284                                              bool & open_font,
285                                              LyXLayout const & style,
286                                              pos_type & i,
287                                              int & column,
288                                              value_type const c)
289 {
290         if (style.pass_thru) {
291                 if (c != '\0') os << c;
292                 return;
293         }
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.
297         switch (c) {
298         case Paragraph::META_INSET: {
299                 Inset * inset = owner_->getInset(i);
300                 if (inset) {
301                         bool close = false;
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()) {
308                                 os << "\\L{";
309                                 close = true;
310                         }
311
312                         int tmp = inset->latex(buf, os, moving_arg,
313                                                style.free_spacing);
314
315                         if (close)
316                                 os << "}";
317
318                         if (tmp) {
319                                 for (int j = 0; j < tmp; ++j) {
320                                         texrow.newline();
321                                 }
322                                 texrow.start(owner_, i + 1);
323                                 column = 0;
324                         } else {
325                                 column += int(os.tellp()) - len;
326                         }
327                 }
328         }
329         break;
330
331         case Paragraph::META_NEWLINE:
332                 if (open_font) {
333                         column += running_font.latexWriteEndChanges(os,
334                                                                     basefont,
335                                                                     basefont);
336                         open_font = false;
337                 }
338                 basefont = owner_->getLayoutFont(bparams);
339                 running_font = basefont;
340                 break;
341
342         case Paragraph::META_HFILL: 
343                 os << "\\hfill{}";
344                 column += 7;
345                 break;
346
347         default:
348                 // And now for the special cases within each mode
349
350                 switch (c) {
351                 case '\\': 
352                         os << "\\textbackslash{}";
353                         column += 15;
354                         break;
355                         
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()
363                               == "latin1" ||
364                               font.language()->encoding()->LatexName()
365                               == "latin9"))) {
366                                 os << "\\ensuremath{"
367                                    << c
368                                    << '}';
369                                 column += 13;
370                         } else {
371                                 os << c;
372                         }
373                         break;
374                         
375                 case '|': case '<': case '>':
376                         // In T1 encoding, these characters exist
377                         if (lyxrc.fontenc == "T1") {
378                                 os << c;
379                                 //... but we should avoid ligatures
380                                 if ((c == '>' || c == '<')
381                                     && i <= size() - 2
382                                     && getChar(i + 1) == c) {
383                                         //os << "\\textcompwordmark{}";
384                                         // Jean-Marc, have a look at
385                                         // this. I think this works
386                                         // equally well:
387                                         os << "\\,{}";
388                                         // Lgb
389                                         column += 19;
390                                 }
391                                 break;
392                         }
393                         // Typewriter font also has them
394                         if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
395                                 os << c;
396                                 break;
397                         } 
398                         // Otherwise, we use what LaTeX
399                         // provides us.
400                         switch (c) {
401                         case '<':
402                                 os << "\\textless{}";
403                                 column += 10;
404                                 break;
405                         case '>':
406                                 os << "\\textgreater{}";
407                                 column += 13;
408                                 break;
409                         case '|':
410                                 os << "\\textbar{}";
411                                 column += 9;
412                                 break;
413                         }
414                         break;
415                         
416                 case '-': // "--" in Typewriter mode -> "-{}-"
417                         if (i <= size() - 2
418                             && getChar(i + 1) == '-'
419                             && font.family() == LyXFont::TYPEWRITER_FAMILY) {
420                                 os << "-{}";
421                                 column += 2;
422                         } else {
423                                 os << '-';
424                         }
425                         break;
426                         
427                 case '\"': 
428                         os << "\\char`\\\"{}";
429                         column += 9;
430                         break;
431                         
432                 case '£':
433                         if (bparams.inputenc == "default") {
434                                 os << "\\pounds{}";
435                                 column += 8;
436                         } else {
437                                 os << c;
438                         }
439                         break;
440                         
441                 case '$': case '&':
442                 case '%': case '#': case '{':
443                 case '}': case '_':
444                         os << '\\' << c;
445                         column += 1;
446                         break;
447                         
448                 case '~':
449                         os << "\\textasciitilde{}";
450                         column += 16;
451                         break;
452                         
453                 case '^':
454                         os << "\\textasciicircum{}";
455                         column += 17;
456                         break;
457                         
458                 case '*': case '[': case ']':
459                         // avoid being mistaken for optional arguments
460                         os << '{' << c << '}';
461                         column += 2;
462                         break;
463
464                 case ' ':
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
469                         // written. (Asger)
470                         break;
471
472                 default:
473  
474                         // I assume this is hack treating typewriter as verbatim
475                         if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
476                                 if (c != '\0') {
477                                         os << c;
478                                 }
479                                 break;
480                         }
481                 
482                         // LyX, LaTeX etc.
483  
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
487  
488                         size_t pnr = 0;
489  
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;
495                                         break;
496                                 }
497                         }
498
499                         if (pnr == phrases_nr && c != '\0') {
500                                 os << c;
501                         }
502                         break;
503                 }
504         }
505 }
506
507
508
509 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
510                                         BufferParams const & bparams,
511                                         std::ostream & os, TexRow & texrow)
512 {
513         lyxerr[Debug::LATEX] << "TeXDeeper...     " << this << std::endl;
514         Paragraph * par = owner_;
515
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,
520                                                   os, texrow);
521                 } else {
522                         par = par->TeXOnePar(buf, bparams,
523                                              os, texrow, false);
524                 }
525         }
526         lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << std::endl;
527
528         return par;
529 }
530
531
532 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
533 {
534         InsetList::const_iterator cit = owner_->insetlist.begin();
535         InsetList::const_iterator lend = owner_->insetlist.end();
536         Paragraph * result;
537         for (; cit != lend; ++cit) {
538                 if ((result = cit->inset->getParFromID(id)))
539                         return result;
540         }
541         return 0;
542 }
543
544
545 LyXFont const Paragraph::Pimpl::realizeFont(LyXFont const & font,
546                                             BufferParams const & bparams) const
547 {
548         LyXFont tmpfont(font);
549         
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();
555                 if (par) {
556                         tmpfont.realize(textclasslist.
557                                         Style(bparams.textclass,
558                                               par->getLayout()).font
559 #ifdef INHERIT_LANGUAGE
560                                         , bparams.language
561 #endif
562                                         );
563                         par_depth = par->getDepth();
564                 }
565         }
566
567         tmpfont.realize(textclasslist.TextClass(bparams.textclass)
568                         .defaultfont()
569 #ifdef INHERIT_LANGUAGE
570                 , bparams.language
571 #endif
572                 );
573         return tmpfont; 
574 }