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