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