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