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