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