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