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