]> git.lyx.org Git - lyx.git/blob - src/paragraph_pimpl.C
pass_thru
[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
24 extern int tex_code_break_column;
25
26
27 // Initialization of the counter for the paragraph id's,
28 unsigned int Paragraph::Pimpl::paragraph_id = 0;
29
30 // Initialize static member.
31 ShareContainer<LyXFont> Paragraph::Pimpl::FontTable::container;
32
33
34 Paragraph::Pimpl::Pimpl(Paragraph * owner)
35         : owner_(owner)
36 {
37         inset_owner = 0;
38         id_ = paragraph_id++;
39 }
40
41
42 Paragraph::Pimpl::Pimpl(Paragraph::Pimpl const & p, Paragraph * owner,
43                         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
70 Paragraph::Pimpl::getChar(Paragraph::size_type pos) const
71 {
72         lyx::Assert(pos <= size());
73         // This is stronger, and I belive that this is the assertion
74         // that we should really use. (Lgb)
75         //Assert(pos < size());
76
77         // Then this has no meaning. (Lgb)
78         if (!size() || pos == size()) return '\0';
79         
80         return text[pos];
81 }
82
83
84 void Paragraph::Pimpl::setChar(Paragraph::size_type pos,
85                                Paragraph::value_type c)
86 {
87         text[pos] = c;
88 }
89
90
91 void Paragraph::Pimpl::insertChar(Paragraph::size_type pos,
92                                   Paragraph::value_type c,
93                                   LyXFont const & font)
94 {
95         lyx::Assert(pos <= size());
96
97         text.insert(text.begin() + pos, c);
98
99         // Update the font table.
100         FontTable search_font(pos, LyXFont());
101         for (FontList::iterator it = std::lower_bound(fontlist.begin(),
102                                                       fontlist.end(),
103                                                       search_font, matchFT());
104              it != fontlist.end(); ++it)
105                 it->pos(it->pos() + 1);
106    
107         // Update the inset table.
108         InsetTable search_inset(pos, 0);
109         for (InsetList::iterator it = std::lower_bound(owner_->insetlist.begin(),
110                                                        owner_->insetlist.end(),
111                                                        search_inset, matchIT());
112              it != owner_->insetlist.end(); ++it)
113                 ++it->pos;
114
115         owner_->setFont(pos, font);
116 }
117
118
119 void Paragraph::Pimpl::insertInset(Paragraph::size_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(Paragraph::size_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                                        Paragraph::size_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 #ifndef NO_LATEX
217             // In LaTeX mode, we don't want to
218             // break lines since some commands
219             // do not like this
220             && ! (font.latex() == LyXFont::ON)
221 #endif
222             // same in FreeSpacing mode
223             && !style.free_spacing
224             // In typewriter mode, we want to avoid 
225             // ! . ? : at the end of a line
226             && !(font.family() == LyXFont::TYPEWRITER_FAMILY
227                  && (getChar(i - 1) == '.'
228                      || getChar(i - 1) == '?' 
229                      || getChar(i - 1) == ':'
230                      || getChar(i - 1) == '!'))) {
231                 if (tex_code_break_column == 0) {
232                         // in batchmode we need LaTeX to still
233                         // see it as a space not as an extra '\n'
234                         os << " %\n";
235                 } else {
236                         os << '\n';
237                 }
238                 texrow.newline();
239                 texrow.start(owner_, i + 1);
240                 column = 0;
241         } else
242 #ifndef NO_LATEX
243                 if (font.latex() == LyXFont::OFF) {
244 #endif
245                 if (style.free_spacing) {
246                         os << '~';
247                 } else {
248                         os << ' ';
249                 }
250 #ifndef NO_LATEX
251         }
252 #endif
253 }
254
255
256 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
257                                              BufferParams const & bparams,
258                                              std::ostream & os,
259                                              TexRow & texrow,
260                                              bool moving_arg,
261                                              LyXFont & font,
262                                              LyXFont & running_font,
263                                              LyXFont & basefont,
264                                              bool & open_font,
265                                              LyXLayout const & style,
266                                              Paragraph::size_type & i,
267                                              int & column,
268                                              Paragraph::value_type const c)
269 {
270         if (style.pass_thru) {
271                 if (c != '\0') os << c;
272                 return;
273         }
274         // Two major modes:  LaTeX or plain
275         // Handle here those cases common to both modes
276         // and then split to handle the two modes separately.
277         switch (c) {
278         case Paragraph::META_INSET: {
279                 Inset * inset = owner_->getInset(i);
280                 if (inset) {
281                         bool close = false;
282                         int const len = os.tellp();
283                         //ostream::pos_type const len = os.tellp();
284                         if ((inset->lyxCode() == Inset::GRAPHICS_CODE
285                              || inset->lyxCode() == Inset::MATH_CODE
286                              || inset->lyxCode() == Inset::URL_CODE)
287                             && running_font.isRightToLeft()) {
288                                 os << "\\L{";
289                                 close = true;
290                         }
291
292                         int tmp = inset->latex(buf, os, moving_arg,
293                                                style.free_spacing);
294
295                         if (close)
296                                 os << "}";
297
298                         if (tmp) {
299                                 column = 0;
300                         } else {
301                                 column += int(os.tellp()) - len;
302                         }
303                         for (; tmp--;) {
304                                 texrow.newline();
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_->getFont(bparams, -1);
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 #ifndef NO_LATEX
329                 // Are we in LaTeX mode?
330                 if (font.latex() == LyXFont::ON) {
331                         // at present we only have one option
332                         // but I'll leave it as a switch statement
333                         // so its simpler to extend. (ARRae)
334                         switch (c) {
335                         default:
336                                 // make sure that we will not print
337                                 // error generating chars to the tex
338                                 // file. This test would not be needed
339                                 // if it were done in the buffer
340                                 // itself.
341                                 if (c != '\0') {
342                                         os << c;
343                                 }
344                                 break;
345                         }
346                 } else {
347 #endif
348                         // Plain mode (i.e. not LaTeX)
349                         switch (c) {
350                         case '\\': 
351                                 os << "\\textbackslash{}";
352                                 column += 15;
353                                 break;
354                 
355                         case '°': case '±': case '²': case '³':  
356                         case '×': case '÷': case '¹': case 'ª':
357                         case 'º': case '¬': case 'µ':
358                                 if (bparams.inputenc == "latin1" ||
359                                     (bparams.inputenc == "auto" &&
360                                      font.language()->encoding()->LatexName()
361                                      == "latin1")) {
362                                         os << "\\ensuremath{"
363                                            << c
364                                            << '}';
365                                         column += 13;
366                                 } else {
367                                         os << c;
368                                 }
369                                 break;
370
371                         case '|': case '<': case '>':
372                                 // In T1 encoding, these characters exist
373                                 if (lyxrc.fontenc == "T1") {
374                                         os << c;
375                                         //... but we should avoid ligatures
376                                         if ((c == '>' || c == '<')
377                                             && i <= size() - 2
378                                             && getChar(i + 1) == c) {
379                                                 //os << "\\textcompwordmark{}";
380                                                 // Jean-Marc, have a look at
381                                                 // this. I think this works
382                                                 // equally well:
383                                                 os << "\\,{}";
384                                                 // Lgb
385                                                 column += 19;
386                                         }
387                                         break;
388                                 }
389                                 // Typewriter font also has them
390                                 if (font.family() == LyXFont::TYPEWRITER_FAMILY) {
391                                         os << c;
392                                         break;
393                                 } 
394                                 // Otherwise, we use what LaTeX
395                                 // provides us.
396                                 switch (c) {
397                                 case '<':
398                                         os << "\\textless{}";
399                                         column += 10;
400                                         break;
401                                 case '>':
402                                         os << "\\textgreater{}";
403                                         column += 13;
404                                         break;
405                                 case '|':
406                                         os << "\\textbar{}";
407                                         column += 9;
408                                         break;
409                                 }
410                                 break;
411
412                         case '-': // "--" in Typewriter mode -> "-{}-"
413                                 if (i <= size() - 2
414                                     && getChar(i + 1) == '-'
415                                     && font.family() == LyXFont::TYPEWRITER_FAMILY) {
416                                         os << "-{}";
417                                         column += 2;
418                                 } else {
419                                         os << '-';
420                                 }
421                                 break;
422
423                         case '\"': 
424                                 os << "\\char`\\\"{}";
425                                 column += 9;
426                                 break;
427
428                         case '£':
429                                 if (bparams.inputenc == "default") {
430                                         os << "\\pounds{}";
431                                         column += 8;
432                                 } else {
433                                         os << c;
434                                 }
435                                 break;
436
437                         case '$': case '&':
438                         case '%': case '#': case '{':
439                         case '}': case '_':
440                                 os << '\\' << c;
441                                 column += 1;
442                                 break;
443
444                         case '~':
445                                 os << "\\textasciitilde{}";
446                                 column += 16;
447                                 break;
448
449                         case '^':
450                                 os << "\\textasciicircum{}";
451                                 column += 17;
452                                 break;
453
454                         case '*': case '[': case ']':
455                                 // avoid being mistaken for optional arguments
456                                 os << '{' << c << '}';
457                                 column += 2;
458                                 break;
459
460                         case ' ':
461                                 // Blanks are printed before font switching.
462                                 // Sure? I am not! (try nice-latex)
463                                 // I am sure it's correct. LyX might be smarter
464                                 // in the future, but for now, nothing wrong is
465                                 // written. (Asger)
466                                 break;
467
468                         default:
469                                 /* idea for labels --- begin*/
470                                 // Check for "LyX"
471                                 if (c ==  'L'
472                                     && i <= size() - 3
473                                     && font.family() != LyXFont::TYPEWRITER_FAMILY
474                                     && getChar(i + 1) == 'y'
475                                     && getChar(i + 2) == 'X') {
476                                         os << "\\LyX{}";
477                                         i += 2;
478                                         column += 5;
479                                 }
480                                 // Check for "TeX"
481                                 else if (c == 'T'
482                                          && i <= size() - 3
483                                          && font.family() != LyXFont::TYPEWRITER_FAMILY
484                                          && getChar(i + 1) == 'e'
485                                          && getChar(i + 2) == 'X') {
486                                         os << "\\TeX{}";
487                                         i += 2;
488                                         column += 5;
489                                 }
490                                 // Check for "LaTeX2e"
491                                 else if (c == 'L'
492                                          && i <= size() - 7
493                                          && font.family() != LyXFont::TYPEWRITER_FAMILY
494                                          && getChar(i + 1) == 'a'
495                                          && getChar(i + 2) == 'T'
496                                          && getChar(i + 3) == 'e'
497                                          && getChar(i + 4) == 'X'
498                                          && getChar(i + 5) == '2'
499                                          && getChar(i + 6) == 'e') {
500                                         os << "\\LaTeXe{}";
501                                         i += 6;
502                                         column += 8;
503                                 }
504                                 // Check for "LaTeX"
505                                 else if (c == 'L'
506                                          && i <= size() - 5
507                                          && font.family() != LyXFont::TYPEWRITER_FAMILY
508                                          && getChar(i + 1) == 'a'
509                                          && getChar(i + 2) == 'T'
510                                          && getChar(i + 3) == 'e'
511                                          && getChar(i + 4) == 'X') {
512                                         os << "\\LaTeX{}";
513                                         i += 4;
514                                         column += 7;
515                                         /* idea for labels --- end*/ 
516                                 } else if (c != '\0') {
517                                         os << c;
518                                 }
519                                 break;
520                         }
521 #ifndef NO_LATEX
522                 }
523 #endif
524         }
525 }
526
527
528
529 Paragraph * Paragraph::Pimpl::TeXDeeper(Buffer const * buf,
530                                         BufferParams const & bparams,
531                                         std::ostream & os, TexRow & texrow)
532 {
533         lyxerr[Debug::LATEX] << "TeXDeeper...     " << this << std::endl;
534         Paragraph * par = owner_;
535
536         while (par && par->params().depth() == owner_->params().depth()) {
537                 if (textclasslist.Style(bparams.textclass, 
538                                         par->layout).isEnvironment()) {
539                         par = par->TeXEnvironment(buf, bparams,
540                                                   os, texrow);
541                 } else {
542                         par = par->TeXOnePar(buf, bparams,
543                                              os, texrow, false);
544                 }
545         }
546         lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << std::endl;
547
548         return par;
549 }
550
551
552 Paragraph * Paragraph::Pimpl::getParFromID(int id) const
553 {
554         InsetList::const_iterator cit = owner_->insetlist.begin();
555         InsetList::const_iterator lend = owner_->insetlist.end();
556         Paragraph * result;
557         for (; cit != lend; ++cit) {
558                 if ((result = cit->inset->getParFromID(id)))
559                         return result;
560         }
561         return 0;
562 }
563