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