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