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