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