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