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