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