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