]> git.lyx.org Git - lyx.git/blob - src/insets/insettext.C
Small compilation fixes for compaq cxx; updated ca.po
[lyx.git] / src / insets / insettext.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ======================================================
4  * 
5  *           LyX, The Document Processor
6  *
7  *           Copyright 1998 The LyX Team.
8  *
9  * ======================================================
10  */
11
12 #include <config.h>
13
14 #include <fstream>
15 using std::ifstream;
16
17 #include <cstdlib>
18
19 #ifdef __GNUG__
20 #pragma implementation
21 #endif
22
23 #include "insettext.h"
24 #include "lyxlex.h"
25 #include "debug.h"
26 #include "lyxfont.h"
27 #include "lyxlex.h"
28 #include "commandtags.h"
29 #include "buffer.h"
30 #include "LyXView.h"
31 #include "BufferView.h"
32 #include "support/textutils.h"
33 #include "layout.h"
34 #include "insetlatexaccent.h"
35 #include "insetquotes.h"
36 #include "mathed/formulamacro.h"
37 #include "figinset.h"
38 #include "insetinfo.h"
39 #include "insetinclude.h"
40 #include "insetbib.h"
41 #include "insetcommand.h"
42 #include "insetindex.h"
43 #include "insetlabel.h"
44 #include "insetref.h"
45 //#include "insettabular.h"
46 #include "insetert.h"
47 #include "insetspecialchar.h"
48 #include "LaTeXFeatures.h"
49 #include "Painter.h"
50 #include "lyx_gui_misc.h"
51 #include "support/LAssert.h"
52
53 extern unsigned char getCurrentTextClass(Buffer *);
54
55 InsetText::InsetText(Buffer * buf)
56 {
57     par = new LyXParagraph();
58     the_locking_inset = 0;
59     buffer = buf;
60     cursor_visible = false;
61     maxWidth = old_x = -1;
62     actpos = selection_start = selection_end = 0;
63     interline_space = 1;
64     no_selection = false;
65     init_inset = true;
66 }
67
68
69 InsetText::InsetText(InsetText const & ins, Buffer * buf)
70 {
71     par = new LyXParagraph(ins.par);
72     the_locking_inset = 0;
73     buffer = buf;
74     cursor_visible = false;
75     maxWidth = old_x = -1;
76     actpos = selection_start = selection_end = 0;
77     interline_space = 1;
78     no_selection = false;
79     init_inset = true;
80 }
81
82
83 InsetText::~InsetText()
84 {
85     delete par;
86 }
87
88
89 Inset * InsetText::Clone() const
90 {
91     InsetText * t = new InsetText(*this, buffer);
92     return t;
93 }
94
95
96 void InsetText::Write(ostream & os) const
97 {
98     os << "Text\n";
99     WriteParagraphData(os);
100 }
101
102
103 void InsetText::WriteParagraphData(ostream & os) const
104 {
105     LyXFont font1 = LyXFont(LyXFont::ALL_INHERIT);
106     LyXFont font2;
107     int column = 0;
108     char c = 0;
109
110     for (int i = 0; i < par->Last(); ++i) {
111         // Write font changes
112         font2 = par->GetFontSettings(i);
113         if (font2 != font1) {
114             font2.lyxWriteChanges(font1, os);
115             column = 0;
116             font1 = font2;
117         }
118         c = par->GetChar(i);
119         // A newline before tags
120         if (column > 0 &&
121             (c == LyXParagraph::META_INSET ||
122              c == LyXParagraph::META_NEWLINE ||
123              c == '\\')) {
124             os << "\n";
125             column = 0;
126         }
127         
128         switch (c) {
129           case LyXParagraph::META_INSET: {
130               Inset * inset = par->GetInset(i);
131               if (inset) {
132                   os << "\n\\begin_inset ";
133                   inset->Write(os);
134                   os << "\n\\end_inset \n\n";
135                   column = 0;
136               }
137           }
138           break;
139           case LyXParagraph::META_NEWLINE: 
140               os << "\\newline\n";
141               column = 0;
142               break;
143           case '\\': 
144               os << "\\backslash\n";
145               column = 0;
146               break;
147           default:
148               if (column > 65 && c==' ') {
149                   os << "\n";
150                   column = 0;
151               }
152               // this check is to amend a bug. LyX sometimes
153               // inserts '\0' this could cause problems.
154               if (c != '\0')
155                   os << c;
156               else
157                       lyxerr << "ERROR (InsetText::writeFile):"
158                               " '\\0' char in structure.\n";
159               column++;
160               break;
161         }
162     }
163     // A newline if the last c was not a tag.
164     if (column > 0 &&
165         (c != LyXParagraph::META_INSET &&
166          c != LyXParagraph::META_NEWLINE &&
167          c != '\\')) {
168         os << "\n";
169         column = 0;
170     }
171 }
172
173
174 void InsetText::Read(LyXLex & lex)
175 {
176     string token, tmptok;
177     LyXFont font = LyXFont(LyXFont::ALL_INHERIT);
178     int pos = 0;
179
180     delete par;
181     par = new LyXParagraph;
182     
183     while (lex.IsOK()) {
184         lex.nextToken();
185         token = lex.GetString();
186         if (token.empty())
187             continue;
188         else if (token[0] != '\\') {
189             int n = token.length();
190             for (int i = 0; i < n; ++i) {
191                 par->InsertChar(pos, token[i]);
192                 par->SetFont(pos, font);
193                 ++pos;
194             }
195         } else if (token == "\\newline") {
196             par->InsertChar(pos, LyXParagraph::META_NEWLINE);
197             par->SetFont(pos, font);
198             ++pos;
199         } else if (token == "\\family") { 
200             lex.next();
201             font.setLyXFamily(lex.GetString());
202         } else if (token == "\\series") {
203             lex.next();
204             font.setLyXSeries(lex.GetString());
205         } else if (token == "\\shape") {
206             lex.next();
207             font.setLyXShape(lex.GetString());
208         } else if (token == "\\size") {
209             lex.next();
210             font.setLyXSize(lex.GetString());
211         } else if (token == "\\latex") {
212             lex.next();
213             string tok = lex.GetString();
214             // This is dirty, but gone with LyX3. (Asger)
215             if (tok == "no_latex")
216                 font.setLatex(LyXFont::OFF);
217             else if (tok == "latex")
218                 font.setLatex(LyXFont::ON);
219             else if (tok == "default")
220                 font.setLatex(LyXFont::INHERIT);
221             else
222                 lex.printError("Unknown LaTeX font flag "
223                                "`$$Token'");
224         } else if (token == "\\direction") {
225                 lex.next();
226                 string tok = lex.GetString();
227                 if (tok == "ltr")
228                         font.setDirection(LyXFont::LTR_DIR);
229                 else if (tok == "rtl")
230                         font.setDirection(LyXFont::RTL_DIR);
231                 else if (tok == "default")
232                         font.setDirection(LyXFont::INHERIT_DIR);
233                 else
234                         lex.printError("Unknown font flag "
235                                        "`$$Token'");
236         } else if (token == "\\emph") {
237             lex.next();
238             font.setEmph(font.setLyXMisc(lex.GetString()));
239         } else if (token == "\\bar") {
240             lex.next();
241             string tok = lex.GetString();
242             // This is dirty, but gone with LyX3. (Asger)
243             if (tok == "under")
244                 font.setUnderbar(LyXFont::ON);
245             else if (tok == "no")
246                 font.setUnderbar(LyXFont::OFF);
247             else if (tok == "default")
248                 font.setUnderbar(LyXFont::INHERIT);
249             else
250                 lex.printError("Unknown bar font flag "
251                                "`$$Token'");
252         } else if (token == "\\noun") {
253             lex.next();
254             font.setNoun(font.setLyXMisc(lex.GetString()));
255         } else if (token == "\\color") {
256             lex.next();
257             font.setLyXColor(lex.GetString());
258         } else if (token == "\\begin_inset") {
259            Inset * inset = 0;
260            lex.next();
261            tmptok = lex.GetString();
262            // Test the different insets.
263            if (tmptok == "Quotes") {
264                inset = new InsetQuotes(string());
265                inset->Read(lex);
266            } else if (tmptok == "LaTeXAccent" || tmptok == "\\i") {
267                inset = new InsetLatexAccent;
268                inset->Read(lex);
269            } else if (tmptok == "FormulaMacro") {
270                inset = new InsetFormulaMacro;
271                inset->Read(lex);
272            } else if (tmptok == "Formula") {
273                inset = new InsetFormula;
274                inset->Read(lex);
275            } else if (tmptok == "Figure") {
276                inset = new InsetFig(100,100, buffer);
277                inset->Read(lex);
278 #if 0
279            } else if (tmptok == "Tabular") {
280                inset = new InsetTabular(buffer);
281                inset->Read(lex);
282 #endif
283            } else if (tmptok == "Text") {
284                inset = new InsetText(buffer);
285                inset->Read(lex);
286            } else if (tmptok == "ERT") {
287                inset = new InsetERT(buffer);
288                inset->Read(lex);
289            } else if (tmptok == "Info") {
290                inset = new InsetInfo;
291                inset->Read(lex);
292            } else if (tmptok == "Include") {
293                inset = new InsetInclude(string(), buffer);
294                inset->Read(lex);
295            } else if (tmptok == "LatexCommand") {
296                InsetCommand inscmd;
297                inscmd.Read(lex);
298                if (inscmd.getCmdName()=="cite") {
299                    inset = new InsetCitation(inscmd.getContents(),
300                                              inscmd.getOptions());
301                } else if (inscmd.getCmdName()=="bibitem") {
302                    lex.printError("Wrong place for bibitem");
303                    inset = inscmd.Clone();
304                } else if (inscmd.getCmdName()=="BibTeX") {
305                    inset = new InsetBibtex(inscmd.getContents(),
306                                            inscmd.getOptions(), buffer);
307                } else if (inscmd.getCmdName()=="index") {
308                    inset = new InsetIndex(inscmd.getContents());
309                } else if (inscmd.getCmdName()=="include") {
310                    inset = new InsetInclude(inscmd.getContents(), buffer);
311                } else if (inscmd.getCmdName()=="label") {
312                    inset = new InsetLabel(inscmd.getCommand());
313                } else if (inscmd.getCmdName() == "ref" ||
314                           inscmd.getCmdName() == "pageref") {
315                    inset = new InsetRef(inscmd, buffer);
316                }
317 #if 0  // Is this compatibility code needed (Lgb)
318                else
319                    // The following three are only for compatibility
320                    if (inscmd.getCmdName()=="-") {
321                        inset = new InsetSpecialChar(InsetSpecialChar::HYPHENATION);
322                    } else if (inscmd.getCmdName()=="@.") {
323                        inset = new InsetSpecialChar(InsetSpecialChar::END_OF_SENTENCE);
324                    } else if (inscmd.getCmdName()=="ldots") {
325                        inset = new InsetSpecialChar(InsetSpecialChar::LDOTS);
326                    } else
327                        inset = inscmd.Clone();
328 #endif
329            }
330            if (inset) {
331                par->InsertChar(pos, LyXParagraph::META_INSET);
332                par->InsertInset(pos, inset);
333                par->SetFont(pos, font);
334                ++pos;
335            } else {
336                lex.printError("Unknown inset `$$Token'. "
337                               "Inserting as text.");
338            }
339 #ifndef NO_COMPABILITY
340         } else if (token == "\\hfill") {
341             // now obsolete, but we have a bak compability
342 //            Inset * inset = new InsetSpecialChar(LyXParagraph::META_HFILL);
343 //            par->InsertChar(pos, LyXParagraph::META_INSET);
344 //            par->InsertInset(pos, inset);
345             par->InsertChar(pos, LyXParagraph::META_HFILL);
346             par->SetFont(pos, font);
347             ++pos;
348         } else if (token == "\\protected_separator") {
349             // now obsolete, but we have a back compability
350             par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
351             //Inset * inset = new InsetSpecialChar(LyXParagraph::META_PROTECTED_SEPARATOR);
352 //            par->InsertChar(pos, LyXParagraph::META_INSET);
353 //            par->InsertInset(pos, inset);
354             par->SetFont(pos, font);
355             ++pos;
356 #endif
357         } else if (token == "\\bibitem") {  // ale970302
358             if (!par->bibkey)
359                 par->bibkey = new InsetBibKey;
360             par->bibkey->Read(lex);                 
361         }else if (token == "\\backslash") {
362             par->InsertChar(pos, '\\');
363             par->SetFont(pos, font);
364             ++pos;
365         } else if (token == "\\end_inset") {
366             break;
367         } else {
368             lex.printError("Unknown tabular token `$$Token'. Not handled!");
369             break;
370         }
371     }
372     if (token != "\\end_inset") {
373         lex.printError("Missing \\end_inset at this point. "
374                        "Read: `$$Token'");
375     }
376     init_inset = true;
377 }
378
379
380 int InsetText::ascent(Painter &pain, LyXFont const & font) const
381 {
382     if (init_inset) {
383         computeTextRows(pain);
384         init_inset = false;
385     }
386     if (maxAscent)
387         return maxAscent;
388     return font.maxAscent();
389 }
390
391
392 int InsetText::descent(Painter &pain, LyXFont const & font) const
393 {
394     if (init_inset) {
395         computeTextRows(pain);
396         init_inset = false;
397     }
398     if (maxDescent)
399         return maxDescent;
400     return font.maxDescent();
401 }
402
403
404 int InsetText::width(Painter &pain, LyXFont const &) const
405 {
406     if (init_inset) {
407         computeTextRows(pain);
408         init_inset = false;
409     }
410     return insetWidth;
411 }
412
413
414 int InsetText::getMaxWidth(UpdatableInset * inset) const
415 {
416     if (!the_locking_inset) {
417             lyxerr << "Text: No locking inset in this inset.\n";
418         return 0;
419     }
420
421     if (the_locking_inset==inset) 
422         return maxWidth;
423
424     return the_locking_inset->getMaxWidth(inset);
425 }
426
427
428 void InsetText::draw(Painter & pain, LyXFont const & f,
429                      int baseline, float & x) const
430 {
431 //    if (init_inset) {
432         computeTextRows(pain);
433 //      init_inset = false;
434 //    }
435     UpdatableInset::draw(pain, f, baseline, x);
436     
437     bool do_reset_pos = (x != top_x) || (baseline != top_baseline);
438     top_x = int(x);
439     top_baseline = baseline;
440     computeBaselines(baseline);
441     for(unsigned int r = 0; r < rows.size() - 1; ++r) {
442         drawRowSelection(pain, rows[r].pos, rows[r+1].pos, r, 
443                          rows[r].baseline, x);
444         drawRowText(pain, rows[r].pos, rows[r+1].pos, rows[r].baseline, x);
445     }
446     x += insetWidth;
447     if (!the_locking_inset && do_reset_pos) {
448 //        HideInsetCursor(bv);
449 //        resetPos(bv);
450 //        ShowInsetCursor(bv);
451     }
452 }
453
454
455 void InsetText::drawRowSelection(Painter & pain, int startpos, int endpos,
456                                  int row, int baseline, float x) const
457 {
458     if (!hasSelection())
459         return;
460
461     int s_start, s_end;
462     if (selection_start > selection_end) {
463         s_start = selection_end;
464         s_end = selection_start;
465     } else {
466         s_start = selection_start;
467         s_end = selection_end;
468     }
469     if ((s_start > endpos) || (s_end < startpos))
470         return;
471     
472     int esel_x;
473     int ssel_x = esel_x = int(x);
474     LyXFont font;
475     int p = startpos;
476     for(; p < endpos; ++p) {
477         if (p == s_start)
478             ssel_x = int(x);
479         if ((p >= s_start) && (p <= s_end))
480             esel_x = int(x);
481         char ch = par->GetChar(p);
482         font = GetFont(par,p);
483         if (IsFloatChar(ch)) {
484             // skip for now
485         } else if (ch == LyXParagraph::META_INSET) {
486             Inset const * tmpinset = par->GetInset(p);
487             x += tmpinset->width(pain, font);
488         } else {
489             x += pain.width(ch,font);
490         }
491     }
492     if (p == s_start)
493         ssel_x = int(x);
494     if ((p >= s_start) && (p <= s_end))
495         esel_x = int(x);
496     if (ssel_x < esel_x) {
497         pain.fillRectangle(int(ssel_x), baseline-rows[row].asc,
498                            int(esel_x - ssel_x),
499                            rows[row].asc + rows[row].desc,
500                            LColor::selection);
501     }
502 }
503
504
505 void InsetText::drawRowText(Painter & pain, int startpos, int endpos,
506                             int baseline, float x) const
507 {
508     Assert(endpos <= par->Last());
509
510     for(int p = startpos; p < endpos; ++p) {
511         char ch = par->GetChar(p);
512         LyXFont font = GetFont(par,p);
513         if (IsFloatChar(ch)) {
514             // skip for now
515         } else if (ch == LyXParagraph::META_INSET) {
516             Inset * tmpinset = par->GetInset(p);
517             if (tmpinset) 
518                 tmpinset->draw(pain, font, baseline, x);
519         } else {
520             pain.text(int(x), baseline, ch, font);
521             x += pain.width(ch,font);
522         }
523     }
524 }
525
526
527 char const * InsetText::EditMessage() const
528 {
529     return _("Opened Text Inset");
530 }
531
532
533 void InsetText::Edit(BufferView * bv, int x, int y, unsigned int button)
534 {
535     UpdatableInset::Edit(bv, x, y, button);
536
537     bv->lockInset(this);
538     the_locking_inset = 0;
539     inset_pos = inset_x = inset_y = 0;
540     no_selection = true;
541     setPos(bv, x,y);
542     selection_start = selection_end = actpos;
543     current_font = real_current_font = GetFont(par, actpos);
544 }
545
546
547 void InsetText::InsetUnlock(BufferView * bv)
548 {
549     if (the_locking_inset)
550         the_locking_inset->InsetUnlock(bv);
551     HideInsetCursor(bv);
552     if (hasSelection()) {
553         selection_start = selection_end = actpos;
554         bv->updateInset(this, false);
555     }
556     the_locking_inset = 0;
557     no_selection = false;
558 }
559
560
561 bool InsetText::UnlockInsetInInset(BufferView * bv, Inset * inset, bool lr)
562 {
563     if (!the_locking_inset)
564         return false;
565     if (the_locking_inset == inset) {
566         the_locking_inset->InsetUnlock(bv);
567         the_locking_inset = 0;
568         if (lr)
569             moveRight(bv, false);
570         return true;
571     }
572     return the_locking_inset->UnlockInsetInInset(bv, inset,lr);
573 }
574
575
576 bool InsetText::UpdateInsetInInset(BufferView * bv, Inset * inset)
577 {
578     if (!the_locking_inset)
579         return false;
580     if (the_locking_inset != inset)
581         return the_locking_inset->UpdateInsetInInset(bv, inset);
582     float x = inset_x;
583     inset->draw(bv->getPainter(), real_current_font, inset_y, x);
584     bv->updateInset(this, true);
585     return true;
586 }
587
588
589 void InsetText::InsetButtonRelease(BufferView * bv, int x, int y, int button)
590 {
591     if (the_locking_inset) {
592         the_locking_inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
593         return;
594     }
595     no_selection = false;
596 }
597
598
599 void InsetText::InsetButtonPress(BufferView * bv, int x, int y, int button)
600 {
601     if (hasSelection()) {
602         selection_start = selection_end = actpos;
603         bv->updateInset(this, false);
604     }
605     no_selection = false;
606     if (the_locking_inset) {
607         setPos(bv, x,y,false);
608         UpdatableInset
609             *inset=0;
610         if (par->GetChar(actpos)==LyXParagraph::META_INSET)
611             inset=(UpdatableInset*)par->GetInset(actpos);
612         if (the_locking_inset == inset) {
613             the_locking_inset->InsetButtonPress(bv,x-inset_x,y-inset_y,button);
614             return;
615         } else if (inset) {
616             // otherwise unlock the_locking_inset and lock the new inset
617             inset_x = cx-top_x;
618             inset_y = cy;
619             inset_pos = actpos;
620             the_locking_inset->InsetUnlock(bv);
621             the_locking_inset = inset;
622             the_locking_inset->Edit(bv, x - inset_x, y - inset_y, button);
623             return;
624         }
625         // otherwise only unlock the_locking_inset
626         the_locking_inset->InsetUnlock(bv);
627     }
628     HideInsetCursor(bv);
629     the_locking_inset = 0;
630     setPos(bv, x, y);
631     selection_start = selection_end = actpos;
632     if (!the_locking_inset)
633         ShowInsetCursor(bv);
634 }
635
636
637 void InsetText::InsetMotionNotify(BufferView * bv, int x, int y, int button)
638 {
639     if (the_locking_inset) {
640         the_locking_inset->InsetMotionNotify(bv, x - inset_x,
641                                              y - inset_y,button);
642         return;
643     }
644     if (!no_selection) {
645         int old = selection_end;
646         setPos(bv, x, y, false);
647         selection_end = actpos;
648         if (old != selection_end)
649             bv->updateInset(this, false);
650     }
651     no_selection = false;
652 }
653
654
655 void InsetText::InsetKeyPress(XKeyEvent * xke)
656 {
657     if (the_locking_inset) {
658         the_locking_inset->InsetKeyPress(xke);
659         return;
660     }
661 }
662
663
664 UpdatableInset::RESULT InsetText::LocalDispatch(BufferView * bv,
665                                                 int action, string const & arg)
666 {
667     no_selection = false;
668     if (UpdatableInset::LocalDispatch(bv, action, arg)) {
669         resetPos(bv);
670         return DISPATCHED;
671     }
672
673     UpdatableInset::RESULT
674         result=DISPATCHED;
675
676     if ((action < 0) && arg.empty())
677         return FINISHED;
678
679     if ((action != LFUN_DOWN) && (action != LFUN_UP) &&
680         (action != LFUN_DOWNSEL) && (action != LFUN_UPSEL))
681         old_x = -1;
682     if (the_locking_inset) {
683         result = the_locking_inset->LocalDispatch(bv, action, arg);
684         if (result == DISPATCHED) {
685             the_locking_inset->ToggleInsetCursor(bv);
686             bv->updateInset(this, false);
687             the_locking_inset->ToggleInsetCursor(bv);
688             return result;
689         } else if (result == FINISHED) {
690             if ((action == LFUN_RIGHT) || (action == -1)) {
691                 actpos = inset_pos + 1;
692                 resetPos(bv);
693             }
694             the_locking_inset = 0;
695             return DISPATCHED;
696         }
697     }
698     HideInsetCursor(bv);
699     switch (action) {
700         // Normal chars
701       case -1:
702           par->InsertChar(actpos,arg[0]);
703           par->SetFont(actpos,real_current_font);
704           computeTextRows(bv->getPainter());
705           bv->updateInset(this, true);
706           ++actpos;
707           selection_start = selection_end = actpos;
708           resetPos(bv);
709           break;
710         // --- Cursor Movements ---------------------------------------------
711       case LFUN_RIGHTSEL:
712           moveRight(bv, false);
713           selection_end = actpos;
714           bv->updateInset(this, false);
715           break;
716       case LFUN_RIGHT:
717           result= DISPATCH_RESULT(moveRight(bv));
718           if (hasSelection()) {
719               selection_start = selection_end = actpos;
720               bv->updateInset(this, false);
721           } else {
722               selection_start = selection_end = actpos;
723           }
724           break;
725       case LFUN_LEFTSEL:
726           moveLeft(bv, false);
727           selection_end = actpos;
728           bv->updateInset(this, false);
729           break;
730       case LFUN_LEFT:
731           result= DISPATCH_RESULT(moveLeft(bv));
732           if (hasSelection()) {
733               selection_start = selection_end = actpos;
734               bv->updateInset(this, false);
735           } else {
736               selection_start = selection_end = actpos;
737           }
738           break;
739       case LFUN_DOWNSEL:
740           moveDown(bv, false);
741           selection_end = actpos;
742           bv->updateInset(this, false);
743           break;
744       case LFUN_DOWN:
745           result= DISPATCH_RESULT(moveDown(bv));
746           if (hasSelection()) {
747               selection_start = selection_end = actpos;
748               bv->updateInset(this, false);
749           } else {
750               selection_start = selection_end = actpos;
751           }
752           break;
753       case LFUN_UPSEL:
754           moveUp(bv, false);
755           selection_end = actpos;
756           bv->updateInset(this, false);
757           break;
758       case LFUN_UP:
759           result= DISPATCH_RESULT(moveUp(bv));
760           if (hasSelection()) {
761               selection_start = selection_end = actpos;
762               bv->updateInset(this, false);
763           } else {
764               selection_start = selection_end = actpos;
765           }
766           break;
767       case LFUN_BACKSPACE:
768           if (!actpos || par->IsNewline(actpos-1)) {
769               if (hasSelection()) {
770                   selection_start = selection_end = actpos;
771                   bv->updateInset(this, false);
772               }
773               break;
774           }
775           moveLeft(bv);
776       case LFUN_DELETE:
777           if (Delete()) { // we need update
778               selection_start = selection_end = actpos;
779               computeTextRows(bv->getPainter());
780               bv->updateInset(this, true);
781           } else if (hasSelection()) {
782               selection_start = selection_end = actpos;
783               bv->updateInset(this, false);
784           }
785           break;
786       case LFUN_HOME:
787           for(; actpos > rows[actrow].pos; --actpos)
788               cx -= SingleWidth(bv->getPainter(), par, actpos);
789           cx -= SingleWidth(bv->getPainter(), par, actpos);
790           if (hasSelection()) {
791               selection_start = selection_end = actpos;
792               bv->updateInset(this, false);
793           } else {
794               selection_start = selection_end = actpos;
795           }
796           break;
797       case LFUN_END:
798           for(; actpos < rows[actrow + 1].pos; ++actpos)
799               cx += SingleWidth(bv->getPainter(), par, actpos);
800           if (hasSelection()) {
801               selection_start = selection_end = actpos;
802               bv->updateInset(this, false);
803           } else {
804               selection_start = selection_end = actpos;
805           }
806           break;
807       case LFUN_MATH_MODE:   // Open or create a math inset
808           InsertInset(bv, new InsetFormula);
809           if (hasSelection()) {
810               selection_start = selection_end = actpos;
811               bv->updateInset(this, false);
812           } else {
813               selection_start = selection_end = actpos;
814           }
815           return DISPATCHED;
816       default:
817           result = UNDISPATCHED;
818           break;
819     }
820     if (result != FINISHED) {
821         if (!the_locking_inset)
822             ShowInsetCursor(bv);
823     } else
824         bv->unlockInset(this);
825     return result;
826 }
827
828
829 int InsetText::Latex(ostream &os, signed char fragile) const
830 {
831     string fstr;
832
833     int i = Latex(fstr, fragile);
834     os << fstr;
835     return i;
836 }
837
838
839 int InsetText::Latex(string & file, signed char /* fragile */) const
840 {
841     TexRow texrow;
842
843     return par->SimpleTeXOnePar(file, texrow);
844 }
845
846
847 void InsetText::Validate(LaTeXFeatures & features) const
848 {
849     par->validate(features);
850 }
851
852
853 // Returns the width of a character at a certain spot
854 int InsetText::SingleWidth(Painter & pain, LyXParagraph * par, int pos) const
855 {
856     LyXFont font = GetFont(par, pos);
857     char c = par->GetChar(pos);
858
859     if (IsPrintable(c)) {
860         return font.width(c);
861     } else if (c == LyXParagraph::META_INSET) {
862         Inset const * tmpinset=par->GetInset(pos);
863         if (tmpinset)
864             return tmpinset->width(pain, font);
865         else
866             return 0;
867     } else if (IsSeparatorChar(c))
868         c = ' ';
869     else if (IsNewlineChar(c))
870         c = 'n';
871     return font.width(c);
872 }
873
874
875 // Returns the width of a character at a certain spot
876 void InsetText::SingleHeight(Painter & pain, LyXParagraph * par,int pos,
877                              int & asc, int & desc) const
878 {
879     LyXFont font = GetFont(par, pos);
880     char c = par->GetChar(pos);
881
882     asc = desc = 0;
883     if (c == LyXParagraph::META_INSET) {
884         Inset const * tmpinset=par->GetInset(pos);
885         if (tmpinset) {
886             asc = tmpinset->ascent(pain, font);
887             desc = tmpinset->descent(pain, font);
888         }
889     } else {
890         asc = font.maxAscent();
891         desc = font.maxDescent();
892     }
893     return;
894 }
895
896
897 // Gets the fully instantiated font at a given position in a paragraph
898 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
899 // The difference is that this one is used for displaying, and thus we
900 // are allowed to make cosmetic improvements. For instance make footnotes
901 // smaller. (Asger)
902 // If position is -1, we get the layout font of the paragraph.
903 // If position is -2, we get the font of the manual label of the paragraph.
904 LyXFont InsetText::GetFont(LyXParagraph * par, int pos) const
905 {
906     char par_depth = par->GetDepth();
907
908     LyXLayout layout =
909             textclasslist.Style(buffer->params.textclass, par->GetLayout());
910
911     // We specialize the 95% common case:
912     if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
913         if (pos >= 0) {
914             // 95% goes here
915             if (layout.labeltype == LABEL_MANUAL
916                 && pos < BeginningOfMainBody(par)) {
917                 // 1% goes here
918                 return par->GetFontSettings(pos).realize(layout.reslabelfont);
919             } else
920                 return par->GetFontSettings(pos).realize(layout.resfont);
921         } else {
922             // 5% goes here.
923             // process layoutfont for pos == -1 and labelfont for pos < -1
924             if (pos == -1)
925                 return layout.resfont;
926             else
927                 return layout.reslabelfont;
928         }
929     }
930     // The uncommon case need not be optimized as much
931
932     LyXFont layoutfont, tmpfont;
933
934     if (pos >= 0){
935         // 95% goes here
936         if (pos < BeginningOfMainBody(par)) {
937             // 1% goes here
938             layoutfont = layout.labelfont;
939         } else {
940             // 99% goes here
941             layoutfont = layout.font;
942         }
943         tmpfont = par->GetFontSettings(pos);
944         tmpfont.realize(layoutfont);
945     } else{
946         // 5% goes here.
947         // process layoutfont for pos == -1 and labelfont for pos < -1
948         if (pos == -1)
949             tmpfont = layout.font;
950         else
951             tmpfont = layout.labelfont;
952     }
953     
954     // Resolve against environment font information
955     //if (par->GetDepth()){ // already in while condition
956     while (par && par_depth && !tmpfont.resolved()) {
957         par = par->DepthHook(par_depth - 1);
958         if (par) {
959             tmpfont.realize(textclasslist.Style(buffer->params.textclass,
960                                                 par->GetLayout()).font);
961             par_depth = par->GetDepth();
962         }
963     }
964     tmpfont.realize((textclasslist.TextClass(buffer->params.textclass).
965                     defaultfont()));
966     return tmpfont;
967 }
968
969
970 int InsetText::BeginningOfMainBody(LyXParagraph * par) const
971 {
972     if (textclasslist.Style(buffer->params.textclass,
973                        par->GetLayout()).labeltype != LABEL_MANUAL)
974         return 0;
975     else
976         return par->BeginningOfMainBody();
977 }
978
979
980 void InsetText::GetCursorPos(int & x, int & y) const
981 {
982     x = cx;
983     y = cy;
984 }
985
986
987 int InsetText::InsetInInsetY()
988 {
989     if (!the_locking_inset)
990         return 0;
991
992     int
993         y = inset_y;
994     return (y + the_locking_inset->InsetInInsetY());
995 }
996
997
998 void InsetText::ToggleInsetCursor(BufferView * bv)
999 {
1000     if (the_locking_inset) {
1001         the_locking_inset->ToggleInsetCursor(bv);
1002         return;
1003     }
1004
1005     LyXFont font = GetFont(par, actpos);
1006
1007     int asc = font.maxAscent();
1008     int desc = font.maxDescent();
1009   
1010     if (cursor_visible)
1011         bv->hideLockedInsetCursor();
1012     else
1013         bv->showLockedInsetCursor(cx, cy, asc, desc);
1014     cursor_visible = !cursor_visible;
1015 }
1016
1017
1018 void InsetText::ShowInsetCursor(BufferView * bv)
1019 {
1020     if (!cursor_visible) {
1021         LyXFont font = GetFont(par, actpos);
1022         
1023         int asc = font.maxAscent();
1024         int desc = font.maxDescent();
1025         bv->fitLockedInsetCursor(cx, cy, asc, desc);
1026         bv->showLockedInsetCursor(cx, cy, asc, desc);
1027         cursor_visible = true;
1028     }
1029 }
1030
1031
1032 void InsetText::HideInsetCursor(BufferView * bv)
1033 {
1034     if (cursor_visible)
1035         ToggleInsetCursor(bv);
1036 }
1037
1038
1039 void InsetText::setPos(BufferView * bv, int x, int y, bool activate_inset)
1040 {
1041     int ox = x,
1042         oy = y;
1043     // search right X-pos x==0 -> top_x
1044     actpos = actrow = 0;
1045     cy = top_baseline;
1046     y += cy;
1047     for(unsigned int i = 1;
1048         ((cy + rows[i - 1].desc) < y) && (i < rows.size() - 1); ++i) {
1049         cy = rows[i].baseline;
1050         actpos = rows[i].pos;
1051         actrow = i;
1052     }
1053     cy -= top_baseline;
1054     cx = top_x;
1055     x += top_x;
1056
1057     int swh;
1058     int sw = swh = SingleWidth(bv->getPainter(), par,actpos);
1059     if (par->GetChar(actpos)!=LyXParagraph::META_INSET)
1060         swh /= 2;
1061     while ((actpos < (rows[actrow + 1].pos - 1)) && ((cx + swh) < x)) {
1062         cx += sw;
1063         ++actpos;
1064         sw = swh = SingleWidth(bv->getPainter(), par,actpos);
1065         if (par->GetChar(actpos)!=LyXParagraph::META_INSET)
1066             swh /= 2;
1067     }
1068     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
1069         the_locking_inset =
1070                 static_cast<UpdatableInset*>(par->GetInset(actpos));
1071         inset_x = cx - top_x;
1072         inset_y = cy;
1073         inset_pos = actpos;
1074         the_locking_inset->Edit(bv, ox - inset_x, oy - inset_y, 0);
1075     }
1076 }
1077
1078
1079 bool InsetText::moveRight(BufferView * bv, bool activate_inset)
1080 {
1081     if (actpos >= par->Last())
1082         return false;
1083     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
1084         the_locking_inset =
1085                 static_cast<UpdatableInset*>(par->GetInset(actpos));
1086         inset_x = cx - top_x;
1087         inset_y = cy;
1088         inset_pos = actpos;
1089         the_locking_inset->Edit(bv, 0, 0, 0);
1090     } else {
1091         ++actpos;
1092         resetPos(bv);
1093     }
1094     return true;
1095 }
1096
1097
1098 bool InsetText::moveLeft(BufferView * bv, bool activate_inset)
1099 {
1100     if (actpos <= 0)
1101         return false;
1102     --actpos;
1103     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
1104         the_locking_inset =
1105                 static_cast<UpdatableInset*>(par->GetInset(actpos));
1106         resetPos(bv);
1107         inset_x = cx - top_x;
1108         inset_y = cy;
1109         inset_pos = actpos;
1110         the_locking_inset->Edit(bv, the_locking_inset->
1111                                 width(bv->getPainter(), GetFont(par,actpos)),
1112                                 0, 0);
1113     } else {
1114         resetPos(bv);
1115     }
1116     return true;
1117 }
1118
1119
1120 bool InsetText::moveUp(BufferView * bv, bool activate_inset)
1121 {
1122     if (!actrow)
1123         return false;
1124     cy = rows[actrow - 1].baseline - top_baseline;
1125     setPos(bv, cx - top_x, cy, activate_inset);
1126     return true;
1127 }
1128
1129
1130 bool InsetText::moveDown(BufferView * bv, bool activate_inset)
1131 {
1132     if (actrow >= int(rows.size() - 2))
1133         return false;
1134     cy = rows[actrow + 1].baseline - top_baseline;
1135     setPos(bv, cx - top_x, cy, activate_inset);
1136     return true;
1137 }
1138
1139
1140 void InsetText::resetPos(BufferView * bv)
1141 {
1142     int old_pos = actpos;
1143
1144     cy = top_baseline;
1145     actrow = 0;
1146     for(int i = 0; rows[i].pos <= actpos; ++i) {
1147         cy = rows[i].baseline;
1148         actrow = i;
1149     }
1150     cy -= top_baseline;
1151     setPos(bv, 0, cy, false);
1152     cx = top_x;
1153     while(actpos < old_pos) {
1154         cx += SingleWidth(bv->getPainter(), par,actpos);
1155         ++actpos;
1156     }
1157 }
1158
1159
1160 bool InsetText::Delete()
1161 {
1162     /* some insets are undeletable here */
1163     if (par->GetChar(actpos)==LyXParagraph::META_INSET) {
1164         /* force complete redo when erasing display insets */ 
1165         /* this is a cruel mathod but save..... Matthias */ 
1166         if (par->GetInset(actpos)->Deletable() &&
1167             par->GetInset(actpos)->display()) {
1168             par->Erase(actpos);
1169             return true;
1170         }
1171         return false;
1172     }
1173     par->Erase(actpos);
1174     return true;
1175 }
1176
1177
1178 bool InsetText::InsertInset(BufferView * bv, Inset * inset)
1179 {
1180     par->InsertChar(actpos, LyXParagraph::META_INSET);
1181     par->InsertInset(actpos, inset);
1182     computeTextRows(bv->getPainter());
1183     bv->updateInset(this, true);
1184     the_locking_inset = static_cast<UpdatableInset*>(inset);
1185     inset_x = cx - top_x;
1186     inset_y = cy;
1187     inset_pos = actpos;
1188     inset->Edit(bv, 0, 0, 0);
1189     return true;
1190 }
1191
1192
1193 UpdatableInset * InsetText::GetLockingInset()
1194 {
1195     return the_locking_inset ? the_locking_inset->GetLockingInset() : this;
1196 }
1197
1198
1199 void InsetText::SetFont(BufferView * bv, LyXFont const & font, bool toggleall)
1200 {
1201     // if there is no selection just set the current_font
1202     if (!hasSelection()) {
1203         // Determine basis font
1204         LyXFont layoutfont;
1205         if (actpos < BeginningOfMainBody(par))
1206             layoutfont = GetFont(par, -2);
1207         else
1208             layoutfont = GetFont(par, -1);
1209         
1210         // Update current font
1211         real_current_font.update(font, toggleall);
1212         
1213         // Reduce to implicit settings
1214         current_font = real_current_font;
1215         current_font.reduce(layoutfont);
1216         // And resolve it completely
1217         real_current_font.realize(layoutfont);
1218         return;
1219     }
1220     
1221     int s_start, s_end;
1222     if (selection_start > selection_end) {
1223         s_start = selection_end;
1224         s_end = selection_start;
1225     } else {
1226         s_start = selection_start;
1227         s_end = selection_end;
1228     }
1229     LyXFont newfont;
1230     while(s_start < s_end) {
1231         newfont = GetFont(par,s_start);
1232         newfont.update(font, toggleall);
1233         SetCharFont(s_start, newfont);
1234         ++s_start;
1235     }
1236     computeTextRows(bv->getPainter());
1237     bv->updateInset(this, true);
1238 }
1239
1240
1241 void InsetText::SetCharFont(int pos, LyXFont const & f)
1242 {
1243     /* let the insets convert their font */
1244         LyXFont font(f);
1245         
1246     if (par->GetChar(pos) == LyXParagraph::META_INSET) {
1247         if (par->GetInset(pos))
1248             font = par->GetInset(pos)->ConvertFont(font);
1249     }
1250     LyXLayout layout =
1251             textclasslist.Style(buffer->params.textclass,par->GetLayout());
1252
1253     // Get concrete layout font to reduce against
1254     LyXFont layoutfont;
1255
1256     if (pos < BeginningOfMainBody(par))
1257         layoutfont = layout.labelfont;
1258     else
1259         layoutfont = layout.font;
1260
1261
1262     layoutfont.realize((textclasslist.TextClass(buffer->params.textclass).
1263                        defaultfont()));
1264
1265     // Now, reduce font against full layout font
1266     font.reduce(layoutfont);
1267
1268     par->SetFont(pos, font);
1269 }
1270
1271
1272 void InsetText::computeTextRows(Painter & pain) const
1273 {
1274     int p,
1275         nwp = 0,
1276         asc = 0,
1277         desc = 0,
1278         oasc = 0,
1279         odesc = 0,
1280         owidth = 0,
1281         wordAscent,
1282         wordDescent;
1283     row_struct row;
1284
1285     if (rows.size())
1286         rows.erase(rows.begin(),rows.end());
1287     int width = wordAscent = wordDescent = 0;
1288     insetWidth = maxAscent = maxDescent = 0;
1289     row.asc      = 0;
1290     row.desc     = 0;
1291     row.pos      = 0;
1292     row.baseline = 0;
1293     rows.push_back(row);
1294     if (maxWidth < 0) {
1295         for(p=0; p < par->Last(); ++p) {
1296             insetWidth += SingleWidth(pain, par, p);
1297             SingleHeight(pain, par, p, asc, desc);
1298             if (asc > maxAscent)
1299                 maxAscent = asc;
1300             if (desc > maxDescent)
1301                 maxDescent = desc;
1302         }
1303         rows[0].asc = maxAscent;
1304         rows[0].desc = maxDescent;
1305         // alocate a dummy row for the endpos
1306         row.pos = par->Last();
1307         rows.push_back(row);
1308         return;
1309     }
1310     bool is_first_word_in_row = true;
1311
1312     int cw,
1313         lastWordWidth=0;
1314
1315     for(p = 0; p < par->Last(); ++p) {
1316         cw = SingleWidth(pain, par, p);
1317         width += cw;
1318         lastWordWidth += cw;
1319         SingleHeight(pain, par, p, asc, desc);
1320         if (asc > wordAscent)
1321             wordAscent = asc;
1322         if (desc > wordDescent)
1323             wordDescent = desc;
1324         Inset const * inset = 0;
1325         if (((p + 1) < par->Last()) &&
1326             (par->GetChar(p+1)==LyXParagraph::META_INSET))
1327             inset = par->GetInset(p+1);
1328         if (inset && inset->display()) {
1329             if (!is_first_word_in_row && (width >= maxWidth)) {
1330                 // we have to split also the row above
1331                 rows[rows.size()-1].asc = oasc;
1332                 rows[rows.size()-1].desc = odesc;
1333                 row.pos = nwp;
1334                 rows.push_back(row);
1335                 oasc = wordAscent;
1336                 odesc = wordDescent;
1337                 if (insetWidth < owidth)
1338                     insetWidth = owidth;
1339                 width = lastWordWidth;
1340                 lastWordWidth = 0;
1341             } else {
1342                 if (oasc < wordAscent)
1343                     oasc = wordAscent;
1344                 if (odesc < wordDescent)
1345                     odesc = wordDescent;
1346             }
1347             rows[rows.size() - 1].asc = oasc;
1348             rows[rows.size() - 1].desc = odesc;
1349             row.pos = ++p;
1350             rows.push_back(row);
1351             SingleHeight(pain, par, p, asc, desc);
1352             rows[rows.size() - 1].asc = asc;
1353             rows[rows.size() - 1].desc = desc;
1354             row.pos = nwp = p + 1;
1355             rows.push_back(row);
1356             oasc = odesc = width = lastWordWidth = 0;
1357             is_first_word_in_row = true;
1358             wordAscent = wordDescent = 0;
1359             continue;
1360         } else if (par->IsSeparator(p)) {
1361             if (width >= maxWidth) {
1362                 if (is_first_word_in_row) {
1363                     rows[rows.size()-1].asc = wordAscent;
1364                     rows[rows.size()-1].desc = wordDescent;
1365                     row.pos = p+1;
1366                     rows.push_back(row);
1367                     oasc = odesc = width = 0;
1368                 } else {
1369                     rows[rows.size()-1].asc = oasc;
1370                     rows[rows.size()-1].desc = odesc;
1371                     row.pos = nwp;
1372                     rows.push_back(row);
1373                     oasc = wordAscent;
1374                     odesc = wordDescent;
1375                     if (insetWidth < owidth)
1376                         insetWidth = owidth;
1377                     width = lastWordWidth;
1378                 }
1379                 wordAscent = wordDescent = lastWordWidth = 0;
1380                 nwp = p+1;
1381                 continue;
1382             }
1383             owidth = width;
1384             if (oasc < wordAscent)
1385                 oasc = wordAscent;
1386             if (odesc < wordDescent)
1387                 odesc = wordDescent;
1388             wordAscent = wordDescent = lastWordWidth = 0;
1389             nwp = p+1;
1390             is_first_word_in_row = false;
1391         }
1392     }
1393     // if we have some data in the paragraph we have ascent/descent
1394     if (p) {
1395         if (width >= maxWidth) {
1396             // assign upper row
1397             rows[rows.size()-1].asc = oasc;
1398             rows[rows.size()-1].desc = odesc;
1399             // assign and allocate lower row
1400             row.pos = nwp;
1401             rows.push_back(row);
1402             rows[rows.size()-1].asc = wordAscent;
1403             rows[rows.size()-1].desc = wordDescent;
1404             if (insetWidth < owidth)
1405                 insetWidth = owidth;
1406             width -= owidth;
1407             if (insetWidth < width)
1408                 insetWidth = width;
1409         } else {
1410             // assign last row data
1411             if (oasc < wordAscent)
1412                 oasc = wordAscent;
1413             if (odesc < wordDescent)
1414                 odesc = wordDescent;
1415             rows[rows.size()-1].asc = oasc;
1416             rows[rows.size()-1].desc = odesc;
1417         }
1418     }
1419     // alocate a dummy row for the endpos
1420     row.pos = par->Last();
1421     rows.push_back(row);
1422     // calculate maxAscent/Descent
1423     maxAscent = rows[0].asc;
1424     maxDescent = rows[0].desc;
1425     for (unsigned int i=1; i<rows.size()-1; ++i) {
1426         maxDescent += rows[i].asc + rows[i].desc + interline_space;
1427     }
1428 #if 0
1429     if (the_locking_inset) {
1430         computeBaselines(top_baseline);
1431         actpos = inset_pos;
1432         resetPos(bv);
1433         inset_x = cx-top_x;
1434         inset_y = cy;
1435     }
1436 #endif
1437 }
1438
1439
1440 void InsetText::computeBaselines(int baseline) const
1441 {
1442     rows[0].baseline = baseline;
1443     for (unsigned int i=1; i<rows.size()-1; i++) {
1444         rows[i].baseline = rows[i-1].baseline + rows[i-1].desc + 
1445             rows[i].asc + interline_space;
1446     }
1447 }