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