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