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