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