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