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