]> git.lyx.org Git - lyx.git/blob - src/insets/insettext.C
Small fixes
[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 #if 0
351             par->InsertChar(pos, LyXParagraph::META_PROTECTED_SEPARATOR);
352 #else
353             Inset * inset =
354                    new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
355             par->InsertChar(pos, LyXParagraph::META_INSET);
356             par->InsertInset(pos, inset);
357 #endif
358             par->SetFont(pos, font);
359             ++pos;
360 #endif
361         } else if (token == "\\bibitem") {  // ale970302
362             if (!par->bibkey)
363                 par->bibkey = new InsetBibKey;
364             par->bibkey->Read(lex);                 
365         } else if (token == "\\backslash") {
366             par->InsertChar(pos, '\\');
367             par->SetFont(pos, font);
368             ++pos;
369         } else if (token == "\\end_inset") {
370             break;
371         } else {
372             lex.printError("Unknown tabular token `$$Token'. Not handled!");
373             break;
374         }
375     }
376     if (token != "\\end_inset") {
377         lex.printError("Missing \\end_inset at this point. "
378                        "Read: `$$Token'");
379     }
380     init_inset = true;
381 }
382
383
384 int InsetText::ascent(Painter & pain, LyXFont const & font) const
385 {
386     if (init_inset) {
387         computeTextRows(pain);
388         init_inset = false;
389     }
390     if (maxAscent)
391         return maxAscent;
392     return font.maxAscent();
393 }
394
395
396 int InsetText::descent(Painter & pain, LyXFont const & font) const
397 {
398     if (init_inset) {
399         computeTextRows(pain);
400         init_inset = false;
401     }
402     if (maxDescent)
403         return maxDescent;
404     return font.maxDescent();
405 }
406
407
408 int InsetText::width(Painter & pain, LyXFont const &) const
409 {
410     if (init_inset) {
411         computeTextRows(pain);
412         init_inset = false;
413     }
414     return insetWidth;
415 }
416
417
418 int InsetText::getMaxWidth(UpdatableInset * inset) const
419 {
420     if (!the_locking_inset) {
421             lyxerr << "Text: No locking inset in this inset.\n";
422         return 0;
423     }
424
425     if (the_locking_inset == inset) 
426         return maxWidth;
427
428     return the_locking_inset->getMaxWidth(inset);
429 }
430
431
432 void InsetText::draw(Painter & pain, LyXFont const & f,
433                      int baseline, float & x) const
434 {
435 //    if (init_inset) {
436         computeTextRows(pain);
437 //      init_inset = false;
438 //    }
439     UpdatableInset::draw(pain, f, baseline, x);
440     
441     bool do_reset_pos = (x != top_x) || (baseline != top_baseline);
442     top_x = int(x);
443     top_baseline = baseline;
444     computeBaselines(baseline);
445     for(unsigned int r = 0; r < rows.size() - 1; ++r) {
446         drawRowSelection(pain, rows[r].pos, rows[r + 1].pos, r, 
447                          rows[r].baseline, x);
448         drawRowText(pain, rows[r].pos, rows[r + 1].pos, rows[r].baseline, x);
449     }
450     x += insetWidth;
451     if (!the_locking_inset && do_reset_pos) {
452 //        HideInsetCursor(bv);
453 //        resetPos(bv);
454 //        ShowInsetCursor(bv);
455     }
456 }
457
458
459 void InsetText::drawRowSelection(Painter & pain, int startpos, int endpos,
460                                  int row, int baseline, float x) const
461 {
462     if (!hasSelection())
463         return;
464
465     int s_start, s_end;
466     if (selection_start > selection_end) {
467         s_start = selection_end;
468         s_end = selection_start;
469     } else {
470         s_start = selection_start;
471         s_end = selection_end;
472     }
473     if ((s_start > endpos) || (s_end < startpos))
474         return;
475     
476     int esel_x;
477     int ssel_x = esel_x = int(x);
478     LyXFont font;
479     int p = startpos;
480     for(; p < endpos; ++p) {
481         if (p == s_start)
482             ssel_x = int(x);
483         if ((p >= s_start) && (p <= s_end))
484             esel_x = int(x);
485         char ch = par->GetChar(p);
486         font = GetFont(par,p);
487         if (IsFloatChar(ch)) {
488             // skip for now
489         } else if (ch == LyXParagraph::META_INSET) {
490             Inset const * tmpinset = par->GetInset(p);
491             x += tmpinset->width(pain, font);
492         } else {
493             x += pain.width(ch,font);
494         }
495     }
496     if (p == s_start)
497         ssel_x = int(x);
498     if ((p >= s_start) && (p <= s_end))
499         esel_x = int(x);
500     if (ssel_x < esel_x) {
501         pain.fillRectangle(int(ssel_x), baseline-rows[row].asc,
502                            int(esel_x - ssel_x),
503                            rows[row].asc + rows[row].desc,
504                            LColor::selection);
505     }
506 }
507
508
509 void InsetText::drawRowText(Painter & pain, int startpos, int endpos,
510                             int baseline, float x) const
511 {
512     Assert(endpos <= par->Last());
513
514     for(int p = startpos; p < endpos; ++p) {
515         char ch = par->GetChar(p);
516         LyXFont font = GetFont(par,p);
517         if (IsFloatChar(ch)) {
518             // skip for now
519         } else if (ch == LyXParagraph::META_INSET) {
520             Inset * tmpinset = par->GetInset(p);
521             if (tmpinset) 
522                 tmpinset->draw(pain, font, baseline, x);
523         } else {
524             pain.text(int(x), baseline, ch, font);
525             x += pain.width(ch,font);
526         }
527     }
528 }
529
530
531 char const * InsetText::EditMessage() const
532 {
533     return _("Opened Text Inset");
534 }
535
536
537 void InsetText::Edit(BufferView * bv, int x, int y, unsigned int button)
538 {
539     UpdatableInset::Edit(bv, x, y, button);
540
541     bv->lockInset(this);
542     the_locking_inset = 0;
543     inset_pos = inset_x = inset_y = 0;
544     no_selection = true;
545     setPos(bv, x,y);
546     selection_start = selection_end = actpos;
547     current_font = real_current_font = GetFont(par, actpos);
548 }
549
550
551 void InsetText::InsetUnlock(BufferView * bv)
552 {
553     if (the_locking_inset)
554         the_locking_inset->InsetUnlock(bv);
555     HideInsetCursor(bv);
556     if (hasSelection()) {
557         selection_start = selection_end = actpos;
558         bv->updateInset(this, false);
559     }
560     the_locking_inset = 0;
561     no_selection = false;
562 }
563
564
565 bool InsetText::UnlockInsetInInset(BufferView * bv, Inset * inset, bool lr)
566 {
567     if (!the_locking_inset)
568         return false;
569     if (the_locking_inset == inset) {
570         the_locking_inset->InsetUnlock(bv);
571         the_locking_inset = 0;
572         if (lr)
573             moveRight(bv, false);
574         return true;
575     }
576     return the_locking_inset->UnlockInsetInInset(bv, inset,lr);
577 }
578
579
580 bool InsetText::UpdateInsetInInset(BufferView * bv, Inset * inset)
581 {
582     if (!the_locking_inset)
583         return false;
584     if (the_locking_inset != inset)
585         return the_locking_inset->UpdateInsetInInset(bv, inset);
586     float x = inset_x;
587     inset->draw(bv->getPainter(), real_current_font, inset_y, x);
588     bv->updateInset(this, true);
589     return true;
590 }
591
592
593 void InsetText::InsetButtonRelease(BufferView * bv, int x, int y, int button)
594 {
595     if (the_locking_inset) {
596         the_locking_inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
597         return;
598     }
599     no_selection = false;
600 }
601
602
603 void InsetText::InsetButtonPress(BufferView * bv, int x, int y, int button)
604 {
605     if (hasSelection()) {
606         selection_start = selection_end = actpos;
607         bv->updateInset(this, false);
608     }
609     no_selection = false;
610     if (the_locking_inset) {
611         setPos(bv, x, y, false);
612         UpdatableInset
613             *inset = 0;
614         if (par->GetChar(actpos) == LyXParagraph::META_INSET)
615             inset = static_cast<UpdatableInset*>(par->GetInset(actpos));
616         if (the_locking_inset == inset) {
617             the_locking_inset->InsetButtonPress(bv,x-inset_x,y-inset_y,button);
618             return;
619         } else if (inset) {
620             // otherwise unlock the_locking_inset and lock the new inset
621             inset_x = cx-top_x;
622             inset_y = cy;
623             inset_pos = actpos;
624             the_locking_inset->InsetUnlock(bv);
625             the_locking_inset = inset;
626             the_locking_inset->Edit(bv, x - inset_x, y - inset_y, button);
627             return;
628         }
629         // otherwise only unlock the_locking_inset
630         the_locking_inset->InsetUnlock(bv);
631     }
632     HideInsetCursor(bv);
633     the_locking_inset = 0;
634     setPos(bv, x, y);
635     selection_start = selection_end = actpos;
636     if (!the_locking_inset)
637         ShowInsetCursor(bv);
638 }
639
640
641 void InsetText::InsetMotionNotify(BufferView * bv, int x, int y, int button)
642 {
643     if (the_locking_inset) {
644         the_locking_inset->InsetMotionNotify(bv, x - inset_x,
645                                              y - inset_y,button);
646         return;
647     }
648     if (!no_selection) {
649         int old = selection_end;
650         setPos(bv, x, y, false);
651         selection_end = actpos;
652         if (old != selection_end)
653             bv->updateInset(this, false);
654     }
655     no_selection = false;
656 }
657
658
659 void InsetText::InsetKeyPress(XKeyEvent * xke)
660 {
661     if (the_locking_inset) {
662         the_locking_inset->InsetKeyPress(xke);
663         return;
664     }
665 }
666
667
668 UpdatableInset::RESULT InsetText::LocalDispatch(BufferView * bv,
669                                                 int action, string const & arg)
670 {
671     no_selection = false;
672     if (UpdatableInset::LocalDispatch(bv, action, arg)) {
673         resetPos(bv);
674         return DISPATCHED;
675     }
676
677     UpdatableInset::RESULT
678         result=DISPATCHED;
679
680     if ((action < 0) && arg.empty())
681         return FINISHED;
682
683     if ((action != LFUN_DOWN) && (action != LFUN_UP) &&
684         (action != LFUN_DOWNSEL) && (action != LFUN_UPSEL))
685         old_x = -1;
686     if (the_locking_inset) {
687         result = the_locking_inset->LocalDispatch(bv, action, arg);
688         if (result == DISPATCHED) {
689             the_locking_inset->ToggleInsetCursor(bv);
690             bv->updateInset(this, false);
691             the_locking_inset->ToggleInsetCursor(bv);
692             return result;
693         } else if (result == FINISHED) {
694             if ((action == LFUN_RIGHT) || (action == -1)) {
695                 actpos = inset_pos + 1;
696                 resetPos(bv);
697             }
698             the_locking_inset = 0;
699             return DISPATCHED;
700         }
701     }
702     HideInsetCursor(bv);
703     switch (action) {
704         // Normal chars
705       case -1:
706           par->InsertChar(actpos,arg[0]);
707           par->SetFont(actpos,real_current_font);
708           computeTextRows(bv->getPainter());
709           bv->updateInset(this, true);
710           ++actpos;
711           selection_start = selection_end = actpos;
712           resetPos(bv);
713           break;
714         // --- Cursor Movements ---------------------------------------------
715       case LFUN_RIGHTSEL:
716           moveRight(bv, false);
717           selection_end = actpos;
718           bv->updateInset(this, false);
719           break;
720       case LFUN_RIGHT:
721           result= DISPATCH_RESULT(moveRight(bv));
722           if (hasSelection()) {
723               selection_start = selection_end = actpos;
724               bv->updateInset(this, false);
725           } else {
726               selection_start = selection_end = actpos;
727           }
728           break;
729       case LFUN_LEFTSEL:
730           moveLeft(bv, false);
731           selection_end = actpos;
732           bv->updateInset(this, false);
733           break;
734       case LFUN_LEFT:
735           result= DISPATCH_RESULT(moveLeft(bv));
736           if (hasSelection()) {
737               selection_start = selection_end = actpos;
738               bv->updateInset(this, false);
739           } else {
740               selection_start = selection_end = actpos;
741           }
742           break;
743       case LFUN_DOWNSEL:
744           moveDown(bv, false);
745           selection_end = actpos;
746           bv->updateInset(this, false);
747           break;
748       case LFUN_DOWN:
749           result= DISPATCH_RESULT(moveDown(bv));
750           if (hasSelection()) {
751               selection_start = selection_end = actpos;
752               bv->updateInset(this, false);
753           } else {
754               selection_start = selection_end = actpos;
755           }
756           break;
757       case LFUN_UPSEL:
758           moveUp(bv, false);
759           selection_end = actpos;
760           bv->updateInset(this, false);
761           break;
762       case LFUN_UP:
763           result= DISPATCH_RESULT(moveUp(bv));
764           if (hasSelection()) {
765               selection_start = selection_end = actpos;
766               bv->updateInset(this, false);
767           } else {
768               selection_start = selection_end = actpos;
769           }
770           break;
771       case LFUN_BACKSPACE:
772           if (!actpos || par->IsNewline(actpos-1)) {
773               if (hasSelection()) {
774                   selection_start = selection_end = actpos;
775                   bv->updateInset(this, false);
776               }
777               break;
778           }
779           moveLeft(bv);
780       case LFUN_DELETE:
781           if (Delete()) { // we need update
782               selection_start = selection_end = actpos;
783               computeTextRows(bv->getPainter());
784               bv->updateInset(this, true);
785           } else if (hasSelection()) {
786               selection_start = selection_end = actpos;
787               bv->updateInset(this, false);
788           }
789           break;
790       case LFUN_HOME:
791           for(; actpos > rows[actrow].pos; --actpos)
792               cx -= SingleWidth(bv->getPainter(), par, actpos);
793           cx -= SingleWidth(bv->getPainter(), par, actpos);
794           if (hasSelection()) {
795               selection_start = selection_end = actpos;
796               bv->updateInset(this, false);
797           } else {
798               selection_start = selection_end = actpos;
799           }
800           break;
801       case LFUN_END:
802           for(; actpos < rows[actrow + 1].pos; ++actpos)
803               cx += SingleWidth(bv->getPainter(), par, actpos);
804           if (hasSelection()) {
805               selection_start = selection_end = actpos;
806               bv->updateInset(this, false);
807           } else {
808               selection_start = selection_end = actpos;
809           }
810           break;
811       case LFUN_MATH_MODE:   // Open or create a math inset
812           InsertInset(bv, new InsetFormula);
813           if (hasSelection()) {
814               selection_start = selection_end = actpos;
815               bv->updateInset(this, false);
816           } else {
817               selection_start = selection_end = actpos;
818           }
819           return DISPATCHED;
820       default:
821           result = UNDISPATCHED;
822           break;
823     }
824     if (result != FINISHED) {
825         if (!the_locking_inset)
826             ShowInsetCursor(bv);
827     } else
828         bv->unlockInset(this);
829     return result;
830 }
831
832
833 int InsetText::Latex(ostream & os, signed char fragile) const
834 {
835     string fstr;
836
837     int i = Latex(fstr, fragile);
838     os << fstr;
839     return i;
840 }
841
842
843 int InsetText::Latex(string & file, signed char /* fragile */) const
844 {
845     TexRow texrow;
846
847     return par->SimpleTeXOnePar(file, texrow);
848 }
849
850
851 void InsetText::Validate(LaTeXFeatures & features) const
852 {
853     par->validate(features);
854 }
855
856
857 // Returns the width of a character at a certain spot
858 int InsetText::SingleWidth(Painter & pain, LyXParagraph * par, int pos) const
859 {
860     LyXFont font = GetFont(par, pos);
861     char c = par->GetChar(pos);
862
863     if (IsPrintable(c)) {
864         return font.width(c);
865     } else if (c == LyXParagraph::META_INSET) {
866         Inset const * tmpinset = par->GetInset(pos);
867         if (tmpinset)
868             return tmpinset->width(pain, font);
869         else
870             return 0;
871     } else if (IsSeparatorChar(c))
872         c = ' ';
873     else if (IsNewlineChar(c))
874         c = 'n';
875     return font.width(c);
876 }
877
878
879 // Returns the width of a character at a certain spot
880 void InsetText::SingleHeight(Painter & pain, LyXParagraph * par,int pos,
881                              int & asc, int & desc) const
882 {
883     LyXFont font = GetFont(par, pos);
884     char c = par->GetChar(pos);
885
886     asc = desc = 0;
887     if (c == LyXParagraph::META_INSET) {
888         Inset const * tmpinset=par->GetInset(pos);
889         if (tmpinset) {
890             asc = tmpinset->ascent(pain, font);
891             desc = tmpinset->descent(pain, font);
892         }
893     } else {
894         asc = font.maxAscent();
895         desc = font.maxDescent();
896     }
897     return;
898 }
899
900
901 // Gets the fully instantiated font at a given position in a paragraph
902 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
903 // The difference is that this one is used for displaying, and thus we
904 // are allowed to make cosmetic improvements. For instance make footnotes
905 // smaller. (Asger)
906 // If position is -1, we get the layout font of the paragraph.
907 // If position is -2, we get the font of the manual label of the paragraph.
908 LyXFont InsetText::GetFont(LyXParagraph * par, int pos) const
909 {
910     char par_depth = par->GetDepth();
911
912     LyXLayout const & layout =
913             textclasslist.Style(buffer->params.textclass, par->GetLayout());
914
915     // We specialize the 95% common case:
916     if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
917         if (pos >= 0) {
918             // 95% goes here
919             if (layout.labeltype == LABEL_MANUAL
920                 && pos < BeginningOfMainBody(par)) {
921                 // 1% goes here
922                 return par->GetFontSettings(pos).realize(layout.reslabelfont);
923             } else
924                 return par->GetFontSettings(pos).realize(layout.resfont);
925         } else {
926             // 5% goes here.
927             // process layoutfont for pos == -1 and labelfont for pos < -1
928             if (pos == -1)
929                 return layout.resfont;
930             else
931                 return layout.reslabelfont;
932         }
933     }
934     // The uncommon case need not be optimized as much
935
936     LyXFont layoutfont, tmpfont;
937
938     if (pos >= 0){
939         // 95% goes here
940         if (pos < BeginningOfMainBody(par)) {
941             // 1% goes here
942             layoutfont = layout.labelfont;
943         } else {
944             // 99% goes here
945             layoutfont = layout.font;
946         }
947         tmpfont = par->GetFontSettings(pos);
948         tmpfont.realize(layoutfont);
949     } else{
950         // 5% goes here.
951         // process layoutfont for pos == -1 and labelfont for pos < -1
952         if (pos == -1)
953             tmpfont = layout.font;
954         else
955             tmpfont = layout.labelfont;
956     }
957     
958     // Resolve against environment font information
959     //if (par->GetDepth()){ // already in while condition
960     while (par && par_depth && !tmpfont.resolved()) {
961         par = par->DepthHook(par_depth - 1);
962         if (par) {
963             tmpfont.realize(textclasslist.Style(buffer->params.textclass,
964                                                 par->GetLayout()).font);
965             par_depth = par->GetDepth();
966         }
967     }
968     tmpfont.realize((textclasslist.TextClass(buffer->params.textclass).
969                     defaultfont()));
970     return tmpfont;
971 }
972
973
974 int InsetText::BeginningOfMainBody(LyXParagraph * par) const
975 {
976     if (textclasslist.Style(buffer->params.textclass,
977                        par->GetLayout()).labeltype != LABEL_MANUAL)
978         return 0;
979     else
980         return par->BeginningOfMainBody();
981 }
982
983
984 void InsetText::GetCursorPos(int & x, int & y) const
985 {
986     x = cx;
987     y = cy;
988 }
989
990
991 int InsetText::InsetInInsetY()
992 {
993     if (!the_locking_inset)
994         return 0;
995
996     int y = inset_y;
997     return (y + the_locking_inset->InsetInInsetY());
998 }
999
1000
1001 void InsetText::ToggleInsetCursor(BufferView * bv)
1002 {
1003     if (the_locking_inset) {
1004         the_locking_inset->ToggleInsetCursor(bv);
1005         return;
1006     }
1007
1008     LyXFont font = GetFont(par, actpos);
1009
1010     int asc = font.maxAscent();
1011     int desc = font.maxDescent();
1012   
1013     if (cursor_visible)
1014         bv->hideLockedInsetCursor();
1015     else
1016         bv->showLockedInsetCursor(cx, cy, asc, desc);
1017     cursor_visible = !cursor_visible;
1018 }
1019
1020
1021 void InsetText::ShowInsetCursor(BufferView * bv)
1022 {
1023     if (!cursor_visible) {
1024         LyXFont font = GetFont(par, actpos);
1025         
1026         int asc = font.maxAscent();
1027         int desc = font.maxDescent();
1028         bv->fitLockedInsetCursor(cx, cy, asc, desc);
1029         bv->showLockedInsetCursor(cx, cy, asc, desc);
1030         cursor_visible = true;
1031     }
1032 }
1033
1034
1035 void InsetText::HideInsetCursor(BufferView * bv)
1036 {
1037     if (cursor_visible)
1038         ToggleInsetCursor(bv);
1039 }
1040
1041
1042 void InsetText::setPos(BufferView * bv, int x, int y, bool activate_inset)
1043 {
1044         int ox = x;
1045         int oy = y;
1046         
1047     // search right X-pos x==0 -> top_x
1048     actpos = actrow = 0;
1049     cy = top_baseline;
1050     y += cy;
1051     for(unsigned int i = 1;
1052         ((cy + rows[i - 1].desc) < y) && (i < rows.size() - 1); ++i) {
1053         cy = rows[i].baseline;
1054         actpos = rows[i].pos;
1055         actrow = i;
1056     }
1057     cy -= top_baseline;
1058     cx = top_x;
1059     x += top_x;
1060
1061     int swh;
1062     int sw = swh = SingleWidth(bv->getPainter(), par,actpos);
1063     if (par->GetChar(actpos)!=LyXParagraph::META_INSET)
1064         swh /= 2;
1065     while ((actpos < (rows[actrow + 1].pos - 1)) && ((cx + swh) < x)) {
1066         cx += sw;
1067         ++actpos;
1068         sw = swh = SingleWidth(bv->getPainter(), par,actpos);
1069         if (par->GetChar(actpos)!=LyXParagraph::META_INSET)
1070             swh /= 2;
1071     }
1072     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
1073         the_locking_inset =
1074                 static_cast<UpdatableInset*>(par->GetInset(actpos));
1075         inset_x = cx - top_x;
1076         inset_y = cy;
1077         inset_pos = actpos;
1078         the_locking_inset->Edit(bv, ox - inset_x, oy - inset_y, 0);
1079     }
1080 }
1081
1082
1083 bool InsetText::moveRight(BufferView * bv, bool activate_inset)
1084 {
1085     if (actpos >= par->Last())
1086         return false;
1087     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
1088         the_locking_inset =
1089                 static_cast<UpdatableInset*>(par->GetInset(actpos));
1090         inset_x = cx - top_x;
1091         inset_y = cy;
1092         inset_pos = actpos;
1093         the_locking_inset->Edit(bv, 0, 0, 0);
1094     } else {
1095         ++actpos;
1096         resetPos(bv);
1097     }
1098     return true;
1099 }
1100
1101
1102 bool InsetText::moveLeft(BufferView * bv, bool activate_inset)
1103 {
1104     if (actpos <= 0)
1105         return false;
1106     --actpos;
1107     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
1108         the_locking_inset =
1109                 static_cast<UpdatableInset*>(par->GetInset(actpos));
1110         resetPos(bv);
1111         inset_x = cx - top_x;
1112         inset_y = cy;
1113         inset_pos = actpos;
1114         the_locking_inset->Edit(bv, the_locking_inset->
1115                                 width(bv->getPainter(), GetFont(par,actpos)),
1116                                 0, 0);
1117     } else {
1118         resetPos(bv);
1119     }
1120     return true;
1121 }
1122
1123
1124 bool InsetText::moveUp(BufferView * bv, bool activate_inset)
1125 {
1126     if (!actrow)
1127         return false;
1128     cy = rows[actrow - 1].baseline - top_baseline;
1129     setPos(bv, cx - top_x, cy, activate_inset);
1130     return true;
1131 }
1132
1133
1134 bool InsetText::moveDown(BufferView * bv, bool activate_inset)
1135 {
1136     if (actrow >= int(rows.size() - 2))
1137         return false;
1138     cy = rows[actrow + 1].baseline - top_baseline;
1139     setPos(bv, cx - top_x, cy, activate_inset);
1140     return true;
1141 }
1142
1143
1144 void InsetText::resetPos(BufferView * bv)
1145 {
1146     int old_pos = actpos;
1147
1148     cy = top_baseline;
1149     actrow = 0;
1150     for(int i = 0; rows[i].pos <= actpos; ++i) {
1151         cy = rows[i].baseline;
1152         actrow = i;
1153     }
1154     cy -= top_baseline;
1155     setPos(bv, 0, cy, false);
1156     cx = top_x;
1157     while(actpos < old_pos) {
1158         cx += SingleWidth(bv->getPainter(), par,actpos);
1159         ++actpos;
1160     }
1161 }
1162
1163
1164 bool InsetText::Delete()
1165 {
1166     /* some insets are undeletable here */
1167     if (par->GetChar(actpos)==LyXParagraph::META_INSET) {
1168         /* force complete redo when erasing display insets */ 
1169         /* this is a cruel mathod but save..... Matthias */ 
1170         if (par->GetInset(actpos)->Deletable() &&
1171             par->GetInset(actpos)->display()) {
1172             par->Erase(actpos);
1173             return true;
1174         }
1175         return false;
1176     }
1177     par->Erase(actpos);
1178     return true;
1179 }
1180
1181
1182 bool InsetText::InsertInset(BufferView * bv, Inset * inset)
1183 {
1184     par->InsertChar(actpos, LyXParagraph::META_INSET);
1185     par->InsertInset(actpos, inset);
1186     computeTextRows(bv->getPainter());
1187     bv->updateInset(this, true);
1188     the_locking_inset = static_cast<UpdatableInset*>(inset);
1189     inset_x = cx - top_x;
1190     inset_y = cy;
1191     inset_pos = actpos;
1192     inset->Edit(bv, 0, 0, 0);
1193     return true;
1194 }
1195
1196
1197 UpdatableInset * InsetText::GetLockingInset()
1198 {
1199     return the_locking_inset ? the_locking_inset->GetLockingInset() : this;
1200 }
1201
1202
1203 void InsetText::SetFont(BufferView * bv, LyXFont const & font, bool toggleall)
1204 {
1205     // if there is no selection just set the current_font
1206     if (!hasSelection()) {
1207         // Determine basis font
1208         LyXFont layoutfont;
1209         if (actpos < BeginningOfMainBody(par))
1210             layoutfont = GetFont(par, -2);
1211         else
1212             layoutfont = GetFont(par, -1);
1213         
1214         // Update current font
1215         real_current_font.update(font, toggleall);
1216         
1217         // Reduce to implicit settings
1218         current_font = real_current_font;
1219         current_font.reduce(layoutfont);
1220         // And resolve it completely
1221         real_current_font.realize(layoutfont);
1222         return;
1223     }
1224     
1225     int s_start, s_end;
1226     if (selection_start > selection_end) {
1227         s_start = selection_end;
1228         s_end = selection_start;
1229     } else {
1230         s_start = selection_start;
1231         s_end = selection_end;
1232     }
1233     LyXFont newfont;
1234     while(s_start < s_end) {
1235         newfont = GetFont(par,s_start);
1236         newfont.update(font, toggleall);
1237         SetCharFont(s_start, newfont);
1238         ++s_start;
1239     }
1240     computeTextRows(bv->getPainter());
1241     bv->updateInset(this, true);
1242 }
1243
1244
1245 void InsetText::SetCharFont(int pos, LyXFont const & f)
1246 {
1247     /* let the insets convert their font */
1248         LyXFont font(f);
1249         
1250     if (par->GetChar(pos) == LyXParagraph::META_INSET) {
1251         if (par->GetInset(pos))
1252             font = par->GetInset(pos)->ConvertFont(font);
1253     }
1254     LyXLayout const & layout =
1255             textclasslist.Style(buffer->params.textclass,par->GetLayout());
1256
1257     // Get concrete layout font to reduce against
1258     LyXFont layoutfont;
1259
1260     if (pos < BeginningOfMainBody(par))
1261         layoutfont = layout.labelfont;
1262     else
1263         layoutfont = layout.font;
1264
1265
1266     layoutfont.realize((textclasslist.TextClass(buffer->params.textclass).
1267                        defaultfont()));
1268
1269     // Now, reduce font against full layout font
1270     font.reduce(layoutfont);
1271
1272     par->SetFont(pos, font);
1273 }
1274
1275
1276 void InsetText::computeTextRows(Painter & pain) const
1277 {
1278     int p,
1279         nwp = 0,
1280         asc = 0,
1281         desc = 0,
1282         oasc = 0,
1283         odesc = 0,
1284         owidth = 0,
1285         wordAscent,
1286         wordDescent;
1287     row_struct row;
1288
1289     if (rows.size())
1290         rows.erase(rows.begin(),rows.end());
1291     int width = wordAscent = wordDescent = 0;
1292     insetWidth = maxAscent = maxDescent = 0;
1293     row.asc      = 0;
1294     row.desc     = 0;
1295     row.pos      = 0;
1296     row.baseline = 0;
1297     rows.push_back(row);
1298     if (maxWidth < 0) {
1299         for(p = 0; p < par->Last(); ++p) {
1300             insetWidth += SingleWidth(pain, par, p);
1301             SingleHeight(pain, par, p, asc, desc);
1302             if (asc > maxAscent)
1303                 maxAscent = asc;
1304             if (desc > maxDescent)
1305                 maxDescent = desc;
1306         }
1307         rows[0].asc = maxAscent;
1308         rows[0].desc = maxDescent;
1309         // alocate a dummy row for the endpos
1310         row.pos = par->Last();
1311         rows.push_back(row);
1312         return;
1313     }
1314     bool is_first_word_in_row = true;
1315
1316     int cw,
1317         lastWordWidth = 0;
1318
1319     for(p = 0; p < par->Last(); ++p) {
1320         cw = SingleWidth(pain, par, p);
1321         width += cw;
1322         lastWordWidth += cw;
1323         SingleHeight(pain, par, p, asc, desc);
1324         if (asc > wordAscent)
1325             wordAscent = asc;
1326         if (desc > wordDescent)
1327             wordDescent = desc;
1328         Inset const * inset = 0;
1329         if (((p + 1) < par->Last()) &&
1330             (par->GetChar(p + 1)==LyXParagraph::META_INSET))
1331             inset = par->GetInset(p + 1);
1332         if (inset && inset->display()) {
1333             if (!is_first_word_in_row && (width >= maxWidth)) {
1334                 // we have to split also the row above
1335                 rows[rows.size() - 1].asc = oasc;
1336                 rows[rows.size() - 1].desc = odesc;
1337                 row.pos = nwp;
1338                 rows.push_back(row);
1339                 oasc = wordAscent;
1340                 odesc = wordDescent;
1341                 if (insetWidth < owidth)
1342                     insetWidth = owidth;
1343                 width = lastWordWidth;
1344                 lastWordWidth = 0;
1345             } else {
1346                 if (oasc < wordAscent)
1347                     oasc = wordAscent;
1348                 if (odesc < wordDescent)
1349                     odesc = wordDescent;
1350             }
1351             rows[rows.size() - 1].asc = oasc;
1352             rows[rows.size() - 1].desc = odesc;
1353             row.pos = ++p;
1354             rows.push_back(row);
1355             SingleHeight(pain, par, p, asc, desc);
1356             rows[rows.size() - 1].asc = asc;
1357             rows[rows.size() - 1].desc = desc;
1358             row.pos = nwp = p + 1;
1359             rows.push_back(row);
1360             oasc = odesc = width = lastWordWidth = 0;
1361             is_first_word_in_row = true;
1362             wordAscent = wordDescent = 0;
1363             continue;
1364         } else if (par->IsSeparator(p)) {
1365             if (width >= maxWidth) {
1366                 if (is_first_word_in_row) {
1367                     rows[rows.size() - 1].asc = wordAscent;
1368                     rows[rows.size() - 1].desc = wordDescent;
1369                     row.pos = p + 1;
1370                     rows.push_back(row);
1371                     oasc = odesc = width = 0;
1372                 } else {
1373                     rows[rows.size() - 1].asc = oasc;
1374                     rows[rows.size() - 1].desc = odesc;
1375                     row.pos = nwp;
1376                     rows.push_back(row);
1377                     oasc = wordAscent;
1378                     odesc = wordDescent;
1379                     if (insetWidth < owidth)
1380                         insetWidth = owidth;
1381                     width = lastWordWidth;
1382                 }
1383                 wordAscent = wordDescent = lastWordWidth = 0;
1384                 nwp = p + 1;
1385                 continue;
1386             }
1387             owidth = width;
1388             if (oasc < wordAscent)
1389                 oasc = wordAscent;
1390             if (odesc < wordDescent)
1391                 odesc = wordDescent;
1392             wordAscent = wordDescent = lastWordWidth = 0;
1393             nwp = p + 1;
1394             is_first_word_in_row = false;
1395         }
1396     }
1397     // if we have some data in the paragraph we have ascent/descent
1398     if (p) {
1399         if (width >= maxWidth) {
1400             // assign upper row
1401             rows[rows.size() - 1].asc = oasc;
1402             rows[rows.size() - 1].desc = odesc;
1403             // assign and allocate lower row
1404             row.pos = nwp;
1405             rows.push_back(row);
1406             rows[rows.size() - 1].asc = wordAscent;
1407             rows[rows.size() - 1].desc = wordDescent;
1408             if (insetWidth < owidth)
1409                 insetWidth = owidth;
1410             width -= owidth;
1411             if (insetWidth < width)
1412                 insetWidth = width;
1413         } else {
1414             // assign last row data
1415             if (oasc < wordAscent)
1416                 oasc = wordAscent;
1417             if (odesc < wordDescent)
1418                 odesc = wordDescent;
1419             rows[rows.size() - 1].asc = oasc;
1420             rows[rows.size() - 1].desc = odesc;
1421         }
1422     }
1423     // alocate a dummy row for the endpos
1424     row.pos = par->Last();
1425     rows.push_back(row);
1426     // calculate maxAscent/Descent
1427     maxAscent = rows[0].asc;
1428     maxDescent = rows[0].desc;
1429     for (unsigned int i = 1; i < rows.size() - 1; ++i) {
1430         maxDescent += rows[i].asc + rows[i].desc + interline_space;
1431     }
1432 #if 0
1433     if (the_locking_inset) {
1434         computeBaselines(top_baseline);
1435         actpos = inset_pos;
1436         resetPos(bv);
1437         inset_x = cx - top_x;
1438         inset_y = cy;
1439     }
1440 #endif
1441 }
1442
1443
1444 void InsetText::computeBaselines(int baseline) const
1445 {
1446     rows[0].baseline = baseline;
1447     for (unsigned int i = 1; i < rows.size() - 1; i++) {
1448         rows[i].baseline = rows[i - 1].baseline + rows[i - 1].desc + 
1449             rows[i].asc + interline_space;
1450     }
1451 }