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