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