]> git.lyx.org Git - lyx.git/blob - src/insets/insettext.C
the freespacing patch from Kayvan, draw the math empty delim with onoffdash, asure...
[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 (ch == LyXParagraph::META_INSET) {
275             Inset * tmpinset = par->GetInset(p);
276             if (tmpinset) 
277                 tmpinset->draw(pain, font, baseline, x);
278         } else {
279             pain.text(int(x), baseline, ch, font);
280             x += pain.width(ch,font);
281         }
282     }
283 }
284
285
286 char const * InsetText::EditMessage() const
287 {
288     return _("Opened Text Inset");
289 }
290
291
292 void InsetText::Edit(BufferView * bv, int x, int y, unsigned int button)
293 {
294     UpdatableInset::Edit(bv, x, y, button);
295
296     bv->lockInset(this);
297     the_locking_inset = 0;
298     inset_pos = inset_x = inset_y = 0;
299     no_selection = true;
300     setPos(bv, x,y);
301     selection_start = selection_end = actpos;
302     current_font = real_current_font = GetFont(par, actpos);
303 }
304
305
306 void InsetText::InsetUnlock(BufferView * bv)
307 {
308     if (the_locking_inset)
309         the_locking_inset->InsetUnlock(bv);
310     HideInsetCursor(bv);
311     if (hasSelection()) {
312         selection_start = selection_end = actpos;
313         UpdateLocal(bv, false);
314     }
315     the_locking_inset = 0;
316     no_selection = false;
317 }
318
319
320 bool InsetText::UnlockInsetInInset(BufferView * bv, Inset * inset, bool lr)
321 {
322     if (!the_locking_inset)
323         return false;
324     if (the_locking_inset == inset) {
325         the_locking_inset->InsetUnlock(bv);
326         the_locking_inset = 0;
327         if (lr)
328             moveRight(bv, false);
329         return true;
330     }
331     return the_locking_inset->UnlockInsetInInset(bv, inset,lr);
332 }
333
334
335 bool InsetText::UpdateInsetInInset(BufferView * bv, Inset * inset)
336 {
337     if (!the_locking_inset)
338         return false;
339     if (the_locking_inset != inset)
340         return the_locking_inset->UpdateInsetInInset(bv, inset);
341     float x = inset_x;
342     inset->draw(bv->getPainter(), real_current_font, inset_y, x);
343     UpdateLocal(bv, true);
344     return true;
345 }
346
347
348 void InsetText::InsetButtonRelease(BufferView * bv, int x, int y, int button)
349 {
350     if (the_locking_inset) {
351         the_locking_inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
352         return;
353     }
354     no_selection = false;
355 }
356
357
358 void InsetText::InsetButtonPress(BufferView * bv, int x, int y, int button)
359 {
360     if (hasSelection()) {
361         selection_start = selection_end = actpos;
362         UpdateLocal(bv, false);
363     }
364     no_selection = false;
365     if (the_locking_inset) {
366         setPos(bv, x, y, false);
367         UpdatableInset
368             *inset = 0;
369         if (par->GetChar(actpos) == LyXParagraph::META_INSET)
370             inset = static_cast<UpdatableInset*>(par->GetInset(actpos));
371         if (the_locking_inset == inset) {
372             the_locking_inset->InsetButtonPress(bv,x-inset_x,y-inset_y,button);
373             return;
374         } else if (inset) {
375             // otherwise unlock the_locking_inset and lock the new inset
376             inset_x = cx-top_x;
377             inset_y = cy;
378             inset_pos = actpos;
379             the_locking_inset->InsetUnlock(bv);
380             the_locking_inset = inset;
381             the_locking_inset->Edit(bv, x - inset_x, y - inset_y, button);
382             return;
383         }
384         // otherwise only unlock the_locking_inset
385         the_locking_inset->InsetUnlock(bv);
386     }
387     HideInsetCursor(bv);
388     the_locking_inset = 0;
389     setPos(bv, x, y);
390     selection_start = selection_end = actpos;
391     if (!the_locking_inset)
392         ShowInsetCursor(bv);
393 }
394
395
396 void InsetText::InsetMotionNotify(BufferView * bv, int x, int y, int button)
397 {
398     if (the_locking_inset) {
399         the_locking_inset->InsetMotionNotify(bv, x - inset_x,
400                                              y - inset_y,button);
401         return;
402     }
403     if (!no_selection) {
404         int old = selection_end;
405         setPos(bv, x, y, false);
406         selection_end = actpos;
407         if (old != selection_end)
408             UpdateLocal(bv, false);
409     }
410     no_selection = false;
411 }
412
413
414 void InsetText::InsetKeyPress(XKeyEvent * xke)
415 {
416     if (the_locking_inset) {
417         the_locking_inset->InsetKeyPress(xke);
418         return;
419     }
420 }
421
422
423 UpdatableInset::RESULT
424 InsetText::LocalDispatch(BufferView * bv,
425                          int action, string const & arg)
426 {
427     no_selection = false;
428     if (UpdatableInset::LocalDispatch(bv, action, arg)) {
429         resetPos(bv);
430         return DISPATCHED;
431     }
432
433     UpdatableInset::RESULT
434         result=DISPATCHED;
435
436     if ((action < 0) && arg.empty())
437         return FINISHED;
438
439     if ((action != LFUN_DOWN) && (action != LFUN_UP) &&
440         (action != LFUN_DOWNSEL) && (action != LFUN_UPSEL))
441         old_x = -1;
442     if (the_locking_inset) {
443         result = the_locking_inset->LocalDispatch(bv, action, arg);
444         if (result == DISPATCHED) {
445             the_locking_inset->ToggleInsetCursor(bv);
446             UpdateLocal(bv, false);
447             the_locking_inset->ToggleInsetCursor(bv);
448             return result;
449         } else if (result == FINISHED) {
450             if ((action == LFUN_RIGHT) || (action == -1)) {
451                 actpos = inset_pos + 1;
452                 resetPos(bv);
453             }
454             the_locking_inset = 0;
455             return DISPATCHED;
456         }
457     }
458     HideInsetCursor(bv);
459     switch (action) {
460         // Normal chars
461       case -1:
462           par->InsertChar(actpos,arg[0]);
463           par->SetFont(actpos,real_current_font);
464           UpdateLocal(bv, true);
465           ++actpos;
466           selection_start = selection_end = actpos;
467           resetPos(bv);
468           break;
469         // --- Cursor Movements ---------------------------------------------
470       case LFUN_RIGHTSEL:
471           moveRight(bv, false);
472           selection_end = actpos;
473           UpdateLocal(bv, false);
474           break;
475       case LFUN_RIGHT:
476           result= DISPATCH_RESULT(moveRight(bv));
477           if (hasSelection()) {
478               selection_start = selection_end = actpos;
479               UpdateLocal(bv, false);
480           } else {
481               selection_start = selection_end = actpos;
482           }
483           break;
484       case LFUN_LEFTSEL:
485           moveLeft(bv, false);
486           selection_end = actpos;
487           UpdateLocal(bv, false);
488           break;
489       case LFUN_LEFT:
490           result= DISPATCH_RESULT(moveLeft(bv));
491           if (hasSelection()) {
492               selection_start = selection_end = actpos;
493               UpdateLocal(bv, false);
494           } else {
495               selection_start = selection_end = actpos;
496           }
497           break;
498       case LFUN_DOWNSEL:
499           moveDown(bv, false);
500           selection_end = actpos;
501           UpdateLocal(bv, false);
502           break;
503       case LFUN_DOWN:
504           result= DISPATCH_RESULT(moveDown(bv));
505           if (hasSelection()) {
506               selection_start = selection_end = actpos;
507               UpdateLocal(bv, false);
508           } else {
509               selection_start = selection_end = actpos;
510           }
511           break;
512       case LFUN_UPSEL:
513           moveUp(bv, false);
514           selection_end = actpos;
515           UpdateLocal(bv, false);
516           break;
517       case LFUN_UP:
518           result= DISPATCH_RESULT(moveUp(bv));
519           if (hasSelection()) {
520               selection_start = selection_end = actpos;
521               UpdateLocal(bv, false);
522           } else {
523               selection_start = selection_end = actpos;
524           }
525           break;
526       case LFUN_BACKSPACE:
527           if (!actpos || par->IsNewline(actpos-1)) {
528               if (hasSelection()) {
529                   selection_start = selection_end = actpos;
530                   UpdateLocal(bv, false);
531               }
532               break;
533           }
534           moveLeft(bv);
535       case LFUN_DELETE:
536           if (Delete()) { // we need update
537               selection_start = selection_end = actpos;
538               UpdateLocal(bv, true);
539           } else if (hasSelection()) {
540               selection_start = selection_end = actpos;
541               UpdateLocal(bv, false);
542           }
543           break;
544       case LFUN_HOME:
545           for(; actpos > rows[actrow].pos; --actpos)
546               cx -= SingleWidth(bv->getPainter(), par, actpos);
547           cx -= SingleWidth(bv->getPainter(), par, actpos);
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_END:
556           for(; actpos < rows[actrow + 1].pos; ++actpos)
557               cx += SingleWidth(bv->getPainter(), par, actpos);
558           if (hasSelection()) {
559               selection_start = selection_end = actpos;
560               UpdateLocal(bv, false);
561           } else {
562               selection_start = selection_end = actpos;
563           }
564           break;
565       case LFUN_MATH_MODE:   // Open or create a math inset
566           InsertInset(bv, new InsetFormula);
567           if (hasSelection()) {
568               selection_start = selection_end = actpos;
569               UpdateLocal(bv, false);
570           } else {
571               selection_start = selection_end = actpos;
572           }
573           return DISPATCHED;
574       default:
575           result = UNDISPATCHED;
576           break;
577     }
578     if (result != FINISHED) {
579         if (!the_locking_inset)
580             ShowInsetCursor(bv);
581     } else
582         bv->unlockInset(this);
583     return result;
584 }
585
586
587 int InsetText::Latex(ostream & os, signed char /*fragile*/,
588 #ifdef USE_OSTREAM_ONLY
589                      bool) const
590 #else
591                      bool free_spc) const
592 #endif
593 {
594 #ifdef USE_OSTREAM_ONLY
595         TexRow texrow;
596         int ret = par->SimpleTeXOnePar(os, texrow);
597         return ret;
598 #else
599     string fstr;
600
601     int i = Latex(fstr, fragile, free_spc);
602     os << fstr;
603     return i;
604 #endif
605 }
606
607
608 #ifndef USE_OSTREAM_ONLY
609 int InsetText::Latex(string & file, signed char /* fragile */) const
610 {
611     TexRow texrow;
612
613     return par->SimpleTeXOnePar(file, texrow);
614 }
615 #endif
616
617
618 void InsetText::Validate(LaTeXFeatures & features) const
619 {
620     par->validate(features);
621 }
622
623
624 // Returns the width of a character at a certain spot
625 int InsetText::SingleWidth(Painter & pain, LyXParagraph * par, int pos) const
626 {
627     LyXFont font = GetFont(par, pos);
628     char c = par->GetChar(pos);
629
630     if (IsPrintable(c)) {
631         return font.width(c);
632     } else if (c == LyXParagraph::META_INSET) {
633         Inset const * tmpinset = par->GetInset(pos);
634         if (tmpinset)
635             return tmpinset->width(pain, font);
636         else
637             return 0;
638     } else if (IsSeparatorChar(c))
639         c = ' ';
640     else if (IsNewlineChar(c))
641         c = 'n';
642     return font.width(c);
643 }
644
645
646 // Returns the width of a character at a certain spot
647 void InsetText::SingleHeight(Painter & pain, LyXParagraph * par,int pos,
648                              int & asc, int & desc) const
649 {
650     LyXFont font = GetFont(par, pos);
651     char c = par->GetChar(pos);
652
653     asc = desc = 0;
654     if (c == LyXParagraph::META_INSET) {
655         Inset const * tmpinset=par->GetInset(pos);
656         if (tmpinset) {
657             asc = tmpinset->ascent(pain, font);
658             desc = tmpinset->descent(pain, font);
659         }
660     } else {
661         asc = font.maxAscent();
662         desc = font.maxDescent();
663     }
664     return;
665 }
666
667
668 // Gets the fully instantiated font at a given position in a paragraph
669 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
670 // The difference is that this one is used for displaying, and thus we
671 // are allowed to make cosmetic improvements. For instance make footnotes
672 // smaller. (Asger)
673 // If position is -1, we get the layout font of the paragraph.
674 // If position is -2, we get the font of the manual label of the paragraph.
675 LyXFont InsetText::GetFont(LyXParagraph * par, int pos) const
676 {
677     char par_depth = par->GetDepth();
678
679     LyXLayout const & layout =
680             textclasslist.Style(buffer->params.textclass, par->GetLayout());
681
682     // We specialize the 95% common case:
683     if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
684         if (pos >= 0) {
685             // 95% goes here
686             if (layout.labeltype == LABEL_MANUAL
687                 && pos < BeginningOfMainBody(par)) {
688                 // 1% goes here
689                 return par->GetFontSettings(pos).realize(layout.reslabelfont);
690             } else
691                 return par->GetFontSettings(pos).realize(layout.resfont);
692         } else {
693             // 5% goes here.
694             // process layoutfont for pos == -1 and labelfont for pos < -1
695             if (pos == -1)
696                 return layout.resfont;
697             else
698                 return layout.reslabelfont;
699         }
700     }
701     // The uncommon case need not be optimized as much
702
703     LyXFont layoutfont, tmpfont;
704
705     if (pos >= 0){
706         // 95% goes here
707         if (pos < BeginningOfMainBody(par)) {
708             // 1% goes here
709             layoutfont = layout.labelfont;
710         } else {
711             // 99% goes here
712             layoutfont = layout.font;
713         }
714         tmpfont = par->GetFontSettings(pos);
715         tmpfont.realize(layoutfont);
716     } else{
717         // 5% goes here.
718         // process layoutfont for pos == -1 and labelfont for pos < -1
719         if (pos == -1)
720             tmpfont = layout.font;
721         else
722             tmpfont = layout.labelfont;
723     }
724     
725     // Resolve against environment font information
726     //if (par->GetDepth()){ // already in while condition
727     while (par && par_depth && !tmpfont.resolved()) {
728         par = par->DepthHook(par_depth - 1);
729         if (par) {
730             tmpfont.realize(textclasslist.Style(buffer->params.textclass,
731                                                 par->GetLayout()).font);
732             par_depth = par->GetDepth();
733         }
734     }
735     tmpfont.realize((textclasslist.TextClass(buffer->params.textclass).
736                     defaultfont()));
737     return tmpfont;
738 }
739
740
741 int InsetText::BeginningOfMainBody(LyXParagraph * par) const
742 {
743     if (textclasslist.Style(buffer->params.textclass,
744                        par->GetLayout()).labeltype != LABEL_MANUAL)
745         return 0;
746     else
747         return par->BeginningOfMainBody();
748 }
749
750
751 void InsetText::GetCursorPos(int & x, int & y) const
752 {
753     x = cx;
754     y = cy;
755 }
756
757
758 int InsetText::InsetInInsetY()
759 {
760     if (!the_locking_inset)
761         return 0;
762
763     int y = inset_y;
764     return (y + the_locking_inset->InsetInInsetY());
765 }
766
767
768 void InsetText::ToggleInsetCursor(BufferView * bv)
769 {
770     if (the_locking_inset) {
771         the_locking_inset->ToggleInsetCursor(bv);
772         return;
773     }
774
775     LyXFont font = GetFont(par, actpos);
776
777     int asc = font.maxAscent();
778     int desc = font.maxDescent();
779   
780     if (cursor_visible)
781         bv->hideLockedInsetCursor();
782     else
783         bv->showLockedInsetCursor(cx, cy, asc, desc);
784     cursor_visible = !cursor_visible;
785 }
786
787
788 void InsetText::ShowInsetCursor(BufferView * bv)
789 {
790     if (!cursor_visible) {
791         LyXFont font = GetFont(par, actpos);
792         
793         int asc = font.maxAscent();
794         int desc = font.maxDescent();
795         bv->fitLockedInsetCursor(cx, cy, asc, desc);
796         bv->showLockedInsetCursor(cx, cy, asc, desc);
797         cursor_visible = true;
798     }
799 }
800
801
802 void InsetText::HideInsetCursor(BufferView * bv)
803 {
804     if (cursor_visible)
805         ToggleInsetCursor(bv);
806 }
807
808
809 void InsetText::setPos(BufferView * bv, int x, int y, bool activate_inset)
810 {
811         int ox = x;
812         int oy = y;
813         
814     // search right X-pos x==0 -> top_x
815     actpos = actrow = 0;
816     cy = top_baseline;
817     y += cy;
818     for(unsigned int i = 1;
819         ((cy + rows[i - 1].desc) < y) && (i < rows.size() - 1); ++i) {
820         cy = rows[i].baseline;
821         actpos = rows[i].pos;
822         actrow = i;
823     }
824     cy -= top_baseline;
825     cx = top_x;
826     x += top_x;
827
828     int swh;
829     int sw = swh = SingleWidth(bv->getPainter(), par,actpos);
830     if (par->GetChar(actpos)!=LyXParagraph::META_INSET)
831         swh /= 2;
832     while ((actpos < (rows[actrow + 1].pos - 1)) && ((cx + swh) < x)) {
833         cx += sw;
834         ++actpos;
835         sw = swh = SingleWidth(bv->getPainter(), par,actpos);
836         if (par->GetChar(actpos)!=LyXParagraph::META_INSET)
837             swh /= 2;
838     }
839     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
840         the_locking_inset =
841                 static_cast<UpdatableInset*>(par->GetInset(actpos));
842         inset_x = cx - top_x;
843         inset_y = cy;
844         inset_pos = actpos;
845         the_locking_inset->Edit(bv, ox - inset_x, oy - inset_y, 0);
846     }
847 }
848
849
850 bool InsetText::moveRight(BufferView * bv, bool activate_inset)
851 {
852     if (actpos >= par->Last())
853         return false;
854     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
855         the_locking_inset =
856                 static_cast<UpdatableInset*>(par->GetInset(actpos));
857         inset_x = cx - top_x;
858         inset_y = cy;
859         inset_pos = actpos;
860         the_locking_inset->Edit(bv, 0, 0, 0);
861     } else {
862         ++actpos;
863         resetPos(bv);
864     }
865     return true;
866 }
867
868
869 bool InsetText::moveLeft(BufferView * bv, bool activate_inset)
870 {
871     if (actpos <= 0)
872         return false;
873     --actpos;
874     if (activate_inset && par->GetChar(actpos)==LyXParagraph::META_INSET) {
875         the_locking_inset =
876                 static_cast<UpdatableInset*>(par->GetInset(actpos));
877         resetPos(bv);
878         inset_x = cx - top_x;
879         inset_y = cy;
880         inset_pos = actpos;
881         the_locking_inset->Edit(bv, the_locking_inset->
882                                 width(bv->getPainter(), GetFont(par,actpos)),
883                                 0, 0);
884     } else {
885         resetPos(bv);
886     }
887     return true;
888 }
889
890
891 bool InsetText::moveUp(BufferView * bv, bool activate_inset)
892 {
893     if (!actrow)
894         return false;
895     cy = rows[actrow - 1].baseline - top_baseline;
896     setPos(bv, cx - top_x, cy, activate_inset);
897     return true;
898 }
899
900
901 bool InsetText::moveDown(BufferView * bv, bool activate_inset)
902 {
903     if (actrow >= int(rows.size() - 2))
904         return false;
905     cy = rows[actrow + 1].baseline - top_baseline;
906     setPos(bv, cx - top_x, cy, activate_inset);
907     return true;
908 }
909
910
911 void InsetText::resetPos(BufferView * bv)
912 {
913     int old_pos = actpos;
914
915     cy = top_baseline;
916     actrow = 0;
917     for(int i = 0; rows[i].pos <= actpos; ++i) {
918         cy = rows[i].baseline;
919         actrow = i;
920     }
921     cy -= top_baseline;
922     setPos(bv, 0, cy, false);
923     cx = top_x;
924     while(actpos < old_pos) {
925         cx += SingleWidth(bv->getPainter(), par,actpos);
926         ++actpos;
927     }
928 }
929
930
931 bool InsetText::Delete()
932 {
933     /* some insets are undeletable here */
934     if (par->GetChar(actpos)==LyXParagraph::META_INSET) {
935         /* force complete redo when erasing display insets */ 
936         /* this is a cruel mathod but save..... Matthias */ 
937         if (par->GetInset(actpos)->Deletable() &&
938             par->GetInset(actpos)->display()) {
939             par->Erase(actpos);
940             return true;
941         }
942         return false;
943     }
944     par->Erase(actpos);
945     return true;
946 }
947
948
949 bool InsetText::InsertInset(BufferView * bv, Inset * inset)
950 {
951     if (inset->Editable() == Inset::IS_EDITABLE) {
952         UpdatableInset *i = (UpdatableInset *)inset;
953         i->setOwner((UpdatableInset *)this);
954     }
955     par->InsertChar(actpos, LyXParagraph::META_INSET);
956     par->InsertInset(actpos, inset);
957     UpdateLocal(bv, true);
958     the_locking_inset = static_cast<UpdatableInset*>(inset);
959     inset_x = cx - top_x;
960     inset_y = cy;
961     inset_pos = actpos;
962     inset->Edit(bv, 0, 0, 0);
963     return true;
964 }
965
966
967 UpdatableInset * InsetText::GetLockingInset()
968 {
969     return the_locking_inset ? the_locking_inset->GetLockingInset() : this;
970 }
971
972
973 void InsetText::SetFont(BufferView * bv, LyXFont const & font, bool toggleall)
974 {
975     // if there is no selection just set the current_font
976     if (!hasSelection()) {
977         // Determine basis font
978         LyXFont layoutfont;
979         if (actpos < BeginningOfMainBody(par))
980             layoutfont = GetFont(par, -2);
981         else
982             layoutfont = GetFont(par, -1);
983         
984         // Update current font
985         real_current_font.update(font, toggleall);
986         
987         // Reduce to implicit settings
988         current_font = real_current_font;
989         current_font.reduce(layoutfont);
990         // And resolve it completely
991         real_current_font.realize(layoutfont);
992         return;
993     }
994     
995     int s_start, s_end;
996     if (selection_start > selection_end) {
997         s_start = selection_end;
998         s_end = selection_start;
999     } else {
1000         s_start = selection_start;
1001         s_end = selection_end;
1002     }
1003     LyXFont newfont;
1004     while(s_start < s_end) {
1005         newfont = GetFont(par,s_start);
1006         newfont.update(font, toggleall);
1007         SetCharFont(s_start, newfont);
1008         ++s_start;
1009     }
1010     UpdateLocal(bv, true);
1011 }
1012
1013
1014 void InsetText::SetCharFont(int pos, LyXFont const & f)
1015 {
1016     /* let the insets convert their font */
1017         LyXFont font(f);
1018         
1019     if (par->GetChar(pos) == LyXParagraph::META_INSET) {
1020         if (par->GetInset(pos))
1021             font = par->GetInset(pos)->ConvertFont(font);
1022     }
1023     LyXLayout const & layout =
1024             textclasslist.Style(buffer->params.textclass,par->GetLayout());
1025
1026     // Get concrete layout font to reduce against
1027     LyXFont layoutfont;
1028
1029     if (pos < BeginningOfMainBody(par))
1030         layoutfont = layout.labelfont;
1031     else
1032         layoutfont = layout.font;
1033
1034
1035     layoutfont.realize((textclasslist.TextClass(buffer->params.textclass).
1036                        defaultfont()));
1037
1038     // Now, reduce font against full layout font
1039     font.reduce(layoutfont);
1040
1041     par->SetFont(pos, font);
1042 }
1043
1044
1045 void InsetText::computeTextRows(Painter & pain, float x) const
1046 {
1047     int p,
1048         nwp = 0,
1049         asc = 0,
1050         desc = 0,
1051         oasc = 0,
1052         odesc = 0,
1053         owidth = 0,
1054         wordAscent,
1055         wordDescent;
1056     row_struct row;
1057
1058     if (rows.size())
1059             rows.clear();
1060     int width = wordAscent = wordDescent = 0;
1061     insetWidth = maxAscent = maxDescent = 0;
1062     row.asc      = 0;
1063     row.desc     = 0;
1064     row.pos      = 0;
1065     row.baseline = 0;
1066     rows.push_back(row);
1067     if (!autoBreakRows) {
1068         for(p = 0; p < par->Last(); ++p) {
1069             insetWidth += SingleWidth(pain, par, p);
1070             SingleHeight(pain, par, p, asc, desc);
1071             maxAscent = max(maxAscent, asc);
1072             maxDescent = max(maxDescent, desc);
1073         }
1074         rows[0].asc = maxAscent;
1075         rows[0].desc = maxDescent;
1076         // alocate a dummy row for the endpos
1077         row.pos = par->Last();
1078         rows.push_back(row);
1079         return;
1080     }
1081
1082     bool is_first_word_in_row = true;
1083
1084     int cw, lastWordWidth = 0;
1085
1086     maxWidth = UpdatableInset::getMaxWidth(pain) - widthOffset;
1087     for(p = 0; p < par->Last(); ++p) {
1088         cw = SingleWidth(pain, par, p);
1089         width += cw;
1090         lastWordWidth += cw;
1091         SingleHeight(pain, par, p, asc, desc);
1092         wordAscent = max(wordAscent, asc);
1093         wordDescent = max(wordDescent, desc);
1094         Inset const * inset = 0;
1095         if (((p + 1) < par->Last()) &&
1096             (par->GetChar(p + 1)==LyXParagraph::META_INSET))
1097             inset = par->GetInset(p + 1);
1098         if (inset && inset->display()) {
1099             if (!is_first_word_in_row && (width >= (maxWidth - x))) {
1100                 // we have to split also the row above
1101                 rows.back().asc = oasc;
1102                 rows.back().desc = odesc;
1103                 row.pos = nwp;
1104                 rows.push_back(row);
1105                 oasc = wordAscent;
1106                 odesc = wordDescent;
1107                 insetWidth = max(insetWidth, owidth);
1108                 width = lastWordWidth;
1109                 lastWordWidth = 0;
1110             } else {
1111                     oasc = max(oasc, wordAscent);
1112                     odesc = max(odesc, wordDescent);
1113             }
1114             rows.back().asc = oasc;
1115             rows.back().desc = odesc;
1116             row.pos = ++p;
1117             rows.push_back(row);
1118             SingleHeight(pain, par, p, asc, desc);
1119             rows.back().asc = asc;
1120             rows.back().desc = desc;
1121             row.pos = nwp = p + 1;
1122             rows.push_back(row);
1123             oasc = odesc = width = lastWordWidth = 0;
1124             is_first_word_in_row = true;
1125             wordAscent = wordDescent = 0;
1126 //          x = 0.0;
1127             continue;
1128         } else if (par->IsSeparator(p)) {
1129             if (width >= maxWidth - x) {
1130                 if (is_first_word_in_row) {
1131                     rows.back().asc = wordAscent;
1132                     rows.back().desc = wordDescent;
1133                     row.pos = p + 1;
1134                     rows.push_back(row);
1135                     oasc = odesc = width = 0;
1136                 } else {
1137                     rows.back().asc = oasc;
1138                     rows.back().desc = odesc;
1139                     row.pos = nwp;
1140                     rows.push_back(row);
1141                     oasc = wordAscent;
1142                     odesc = wordDescent;
1143                     insetWidth = max(insetWidth, owidth);
1144                     width = lastWordWidth;
1145                 }
1146                 wordAscent = wordDescent = lastWordWidth = 0;
1147                 nwp = p + 1;
1148 //              x = 0.0;
1149                 continue;
1150             }
1151             owidth = width;
1152             oasc = max(oasc, wordAscent);
1153             odesc = max(odesc, wordDescent);
1154             wordAscent = wordDescent = lastWordWidth = 0;
1155             nwp = p + 1;
1156             is_first_word_in_row = false;
1157         }
1158     }
1159     // if we have some data in the paragraph we have ascent/descent
1160     if (p) {
1161         if (width >= (maxWidth - x)) {
1162             // assign upper row
1163             rows.back().asc = oasc;
1164             rows.back().desc = odesc;
1165             // assign and allocate lower row
1166             row.pos = nwp;
1167             rows.push_back(row);
1168             rows.back().asc = wordAscent;
1169             rows.back().desc = wordDescent;
1170             width -= lastWordWidth;
1171         } else {
1172             // assign last row data
1173 //          width = lastWordWidth;
1174 //          lastWordWidth = 0;
1175             rows.back().asc = max(oasc, wordAscent);
1176             rows.back().desc = max(odesc, wordDescent);
1177         }
1178     }
1179     insetWidth = max(insetWidth, width);
1180     // alocate a dummy row for the endpos
1181     row.pos = par->Last();
1182     rows.push_back(row);
1183     // calculate maxAscent/Descent
1184     maxAscent = rows[0].asc;
1185     maxDescent = rows[0].desc;
1186     for (RowList::size_type i = 1; i < rows.size() - 1; ++i) {
1187         maxDescent += rows[i].asc + rows[i].desc + interline_space;
1188     }
1189 #if 0
1190     if (the_locking_inset) {
1191         computeBaselines(top_baseline);
1192         actpos = inset_pos;
1193         resetPos(bv);
1194         inset_x = cx - top_x;
1195         inset_y = cy;
1196     }
1197 #endif
1198 }
1199
1200
1201 void InsetText::computeBaselines(int baseline) const
1202 {
1203     rows[0].baseline = baseline;
1204     for (unsigned int i = 1; i < rows.size() - 1; i++) {
1205         rows[i].baseline = rows[i - 1].baseline + rows[i - 1].desc + 
1206             rows[i].asc + interline_space;
1207     }
1208 }
1209
1210 void InsetText::UpdateLocal(BufferView *bv, bool flag)
1211 {
1212     init_inset = flag;
1213     bv->updateInset(this, flag);
1214 }