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