2 /* This file is part of
3 * ======================================================
5 * LyX, The Document Processor
7 * Copyright 1998-2000 The LyX Team.
9 * ======================================================
20 #pragma implementation
23 #include "insettext.h"
28 #include "commandtags.h"
31 #include "BufferView.h"
32 #include "support/textutils.h"
34 #include "insetlatexaccent.h"
35 #include "insetquotes.h"
36 #include "mathed/formulamacro.h"
38 #include "insetinfo.h"
39 #include "insetinclude.h"
41 #include "insetcommand.h"
42 #include "insetindex.h"
43 #include "insetlabel.h"
45 //#include "insettabular.h"
47 #include "insetspecialchar.h"
48 #include "LaTeXFeatures.h"
50 #include "lyx_gui_misc.h"
51 #include "support/LAssert.h"
53 #include "lyxcursor.h"
54 #include "CutAndPaste.h"
63 extern unsigned char getCurrentTextClass(Buffer *);
66 InsetText::InsetText(Buffer * buf)
68 par = new LyXParagraph();
73 InsetText::InsetText(InsetText const & ins, Buffer * buf)
77 autoBreakRows = ins.autoBreakRows;
81 void InsetText::init(Buffer * buf, InsetText const * ins)
83 the_locking_inset = 0;
85 cursor_visible = false;
87 selection_start = selection_end = 0;
91 maxAscent = maxDescent = insetWidth = 0;
92 drawTextXOffset = drawTextYOffset = 0;
93 autoBreakRows = false;
98 par = ins->par->Clone();
99 autoBreakRows = ins->autoBreakRows;
101 par->SetInsetOwner(this);
107 InsetText::~InsetText()
113 Inset * InsetText::Clone() const
115 InsetText * t = new InsetText(*this, buffer);
120 void InsetText::Write(ostream & os) const
123 WriteParagraphData(os);
127 void InsetText::WriteParagraphData(ostream & os) const
129 par->writeFile(os, buffer->params, 0, 0);
133 void InsetText::Read(LyXLex & lex)
135 string token, tmptok;
137 LyXParagraph * return_par = 0;
138 char depth = 0; // signed or unsigned?
139 LyXParagraph::footnote_flag footnoteflag = LyXParagraph::NO_FOOTNOTE;
140 LyXParagraph::footnote_kind footnotekind = LyXParagraph::FOOTNOTE;
141 LyXFont font(LyXFont::ALL_INHERIT);
144 par = new LyXParagraph;
145 par->SetInsetOwner(this);
149 token = lex.GetString();
152 if (token == "\\end_inset")
154 if (buffer->parseSingleLyXformat2Token(lex, par, return_par,
158 // the_end read this should NEVER happen
159 lex.printError("\\the_end read in inset! Error in document!");
163 if (token != "\\end_inset") {
164 lex.printError("Missing \\end_inset at this point. "
171 int InsetText::ascent(Painter & pain, LyXFont const & font) const
174 computeTextRows(pain, xpos);
180 return lyxfont::maxAscent(font);
184 int InsetText::descent(Painter & pain, LyXFont const & font) const
187 computeTextRows(pain, xpos);
193 return lyxfont::maxDescent(font);
197 int InsetText::width(Painter & pain, LyXFont const &) const
200 computeTextRows(pain, xpos);
208 void InsetText::draw(Painter & pain, LyXFont const & f,
209 int baseline, float & x) const
212 UpdatableInset::draw(pain, f, baseline, x);
214 if (init_inset || (baseline != top_baseline) || (top_x != int(x))) {
215 top_baseline = baseline;
216 if (init_inset || (top_x != int(x))) {
218 computeTextRows(pain, x);
221 computeBaselines(baseline);
223 if (the_locking_inset && (cursor.pos == inset_pos)) {
225 inset_x = cursor.x - top_x + drawTextXOffset;
226 inset_y = cursor.y + drawTextYOffset;
228 for(RowList::size_type r = 0; r < rows.size() - 1; ++r) {
229 drawRowSelection(pain, rows[r].pos, rows[r + 1].pos, r,
230 rows[r].baseline, x);
231 drawRowText(pain, rows[r].pos, rows[r + 1].pos, rows[r].baseline, x);
237 void InsetText::drawRowSelection(Painter & pain, int startpos, int endpos,
238 int row, int baseline, float x) const
244 if (selection_start > selection_end) {
245 s_start = selection_end;
246 s_end = selection_start;
248 s_start = selection_start;
249 s_end = selection_end;
251 if ((s_start > endpos) || (s_end < startpos))
255 int ssel_x = esel_x = int(x);
258 for(; p < endpos; ++p) {
261 if ((p >= s_start) && (p <= s_end))
263 char ch = par->GetChar(p);
264 font = GetDrawFont(par,p);
265 if (IsFloatChar(ch)) {
267 } else if (ch == LyXParagraph::META_INSET) {
268 Inset const * tmpinset = par->GetInset(p);
269 x += tmpinset->width(pain, font);
271 x += lyxfont::width(ch, font);
276 if ((p >= s_start) && (p <= s_end))
278 if (ssel_x < esel_x) {
279 pain.fillRectangle(int(ssel_x), baseline-rows[row].asc,
280 int(esel_x - ssel_x),
281 rows[row].asc + rows[row].desc,
287 void InsetText::drawRowText(Painter & pain, int startpos, int endpos,
288 int baseline, float x) const
290 Assert(endpos <= par->Last());
292 for(int p = startpos; p < endpos; ++p) {
293 char ch = par->GetChar(p);
294 LyXFont font = GetDrawFont(par,p);
295 if (IsFloatChar(ch)) {
297 } else if (par->IsNewline(p)) {
298 // Draw end-of-line marker
299 int wid = lyxfont::width('n', font);
300 int asc = lyxfont::maxAscent(font);
304 xp[0] = int(x + wid * 0.375);
305 yp[0] = int(y - 0.875 * asc * 0.75);
308 yp[1] = int(y - 0.500 * asc * 0.75);
310 xp[2] = int(x + wid * 0.375);
311 yp[2] = int(y - 0.125 * asc * 0.75);
313 pain.lines(xp, yp, 3, LColor::eolmarker);
316 yp[0] = int(y - 0.500 * asc * 0.75);
318 xp[1] = int(x + wid);
319 yp[1] = int(y - 0.500 * asc * 0.75);
321 xp[2] = int(x + wid);
322 yp[2] = int(y - asc * 0.75);
324 pain.lines(xp, yp, 3, LColor::eolmarker);
326 } else if (ch == LyXParagraph::META_INSET) {
327 Inset * tmpinset = par->GetInset(p);
329 tmpinset->draw(pain, font, baseline, x);
331 pain.text(int(x), baseline, ch, font);
332 x += lyxfont::width(ch, font);
338 char const * InsetText::EditMessage() const
340 return _("Opened Text Inset");
344 void InsetText::Edit(BufferView * bv, int x, int y, unsigned int button)
346 par->SetInsetOwner(this);
347 UpdatableInset::Edit(bv, x, y, button);
349 if (!bv->lockInset(this)) {
350 lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
353 the_locking_inset = 0;
354 selection_start = selection_end = inset_pos = inset_x = inset_y = 0;
355 // no_selection = true;
356 setPos(bv->painter(), x, y);
357 checkAndActivateInset(bv, x, y, button);
358 selection_start = selection_end = cursor.pos;
359 current_font = real_current_font = GetFont(par, cursor.pos);
360 bv->text->FinishUndo();
361 UpdateLocal(bv, true);
365 void InsetText::InsetUnlock(BufferView * bv)
367 if (the_locking_inset) {
368 the_locking_inset->InsetUnlock(bv);
369 the_locking_inset = 0;
371 lyxerr[Debug::INSETS] << "InsetText::InsetUnlock(" << this <<
373 if (hasSelection()) {
374 selection_start = selection_end = cursor.pos;
375 UpdateLocal(bv, false);
377 no_selection = false;
381 bool InsetText::LockInsetInInset(BufferView * bv, UpdatableInset * inset)
383 lyxerr[Debug::INSETS] << "InsetText::LockInsetInInset(" << inset << "): ";
386 if (inset == par->GetInset(cursor.pos)) {
387 lyxerr[Debug::INSETS] << "OK" << endl;
388 the_locking_inset = inset;
389 resetPos(bv->painter());
390 inset_x = cursor.x - top_x + drawTextXOffset;
391 inset_y = cursor.y + drawTextYOffset;
392 inset_pos = cursor.pos;
394 } else if (the_locking_inset && (the_locking_inset == inset)) {
395 if (cursor.pos == inset_pos) {
396 lyxerr[Debug::INSETS] << "OK" << endl;
397 resetPos(bv->painter());
398 inset_x = cursor.x - top_x + drawTextXOffset;
399 inset_y = cursor.y + drawTextYOffset;
401 lyxerr[Debug::INSETS] << "cursor.pos != inset_pos" << endl;
403 } else if (the_locking_inset) {
404 lyxerr[Debug::INSETS] << "MAYBE" << endl;
405 return the_locking_inset->LockInsetInInset(bv, inset);
407 lyxerr[Debug::INSETS] << "NOT OK" << endl;
412 bool InsetText::UnlockInsetInInset(BufferView * bv, UpdatableInset * inset,
415 if (!the_locking_inset)
417 if (the_locking_inset == inset) {
418 the_locking_inset->InsetUnlock(bv);
419 the_locking_inset = 0;
421 moveRight(bv, false);
424 return the_locking_inset->UnlockInsetInInset(bv, inset, lr);
428 bool InsetText::UpdateInsetInInset(BufferView * bv, Inset * inset)
430 if (!the_locking_inset)
432 if (the_locking_inset != inset)
433 return the_locking_inset->UpdateInsetInInset(bv, inset);
434 lyxerr[Debug::INSETS] << "InsetText::UpdateInsetInInset(" << inset <<
436 UpdateLocal(bv, true);
437 if (cursor.pos == inset_pos) {
438 inset_x = cursor.x - top_x + drawTextXOffset;
439 inset_y = cursor.y + drawTextYOffset;
445 void InsetText::InsetButtonPress(BufferView * bv, int x, int y, int button)
447 if (hasSelection()) {
448 selection_start = selection_end = cursor.pos;
449 UpdateLocal(bv, false);
451 no_selection = false;
452 setPos(bv->painter(), x, y);
454 if (the_locking_inset) {
457 if (par->GetChar(cursor.pos) == LyXParagraph::META_INSET)
458 inset = static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
459 if (the_locking_inset == inset) {
460 the_locking_inset->InsetButtonPress(bv,x-inset_x,y-inset_y,button);
463 // otherwise unlock the_locking_inset and lock the new inset
464 the_locking_inset->InsetUnlock(bv);
465 inset_x = cursor.x - top_x + drawTextXOffset;
466 inset_y = cursor.y + drawTextYOffset;
467 inset->InsetButtonPress(bv, x-inset_x, y-inset_y, button);
468 inset->Edit(bv, x - inset_x, y - inset_y, button);
469 UpdateLocal(bv, true);
472 // otherwise only unlock the_locking_inset
473 the_locking_inset->InsetUnlock(bv);
474 the_locking_inset = 0;
476 if (bv->the_locking_inset) {
477 if ((par->GetChar(cursor.pos) == LyXParagraph::META_INSET) &&
478 par->GetInset(cursor.pos) &&
479 (par->GetInset(cursor.pos)->Editable() == Inset::HIGHLY_EDITABLE)) {
480 UpdatableInset *inset =
481 static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
482 inset_x = cursor.x - top_x + drawTextXOffset;
483 inset_y = cursor.y + drawTextYOffset;
484 inset->InsetButtonPress(bv, x-inset_x, y-inset_y, button);
485 inset->Edit(bv, x-inset_x, y-inset_y, 0);
486 UpdateLocal(bv, true);
489 selection_start = selection_end = cursor.pos;
493 void InsetText::InsetButtonRelease(BufferView * bv, int x, int y, int button)
495 UpdatableInset * inset = 0;
497 if (the_locking_inset) {
498 the_locking_inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
500 if (par->GetChar(cursor.pos) == LyXParagraph::META_INSET) {
501 inset = static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
502 if (inset->Editable()==Inset::HIGHLY_EDITABLE) {
503 inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
505 inset_x = cursor.x - top_x + drawTextXOffset;
506 inset_y = cursor.y + drawTextYOffset;
507 inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
508 inset->Edit(bv, x-inset_x, y-inset_y, button);
512 no_selection = false;
516 void InsetText::InsetMotionNotify(BufferView * bv, int x, int y, int state)
518 if (the_locking_inset) {
519 the_locking_inset->InsetMotionNotify(bv, x - inset_x,
524 int old = selection_end;
526 setPos(bv->painter(), x, y);
527 selection_end = cursor.pos;
528 if (old != selection_end)
529 UpdateLocal(bv, false);
532 no_selection = false;
536 void InsetText::InsetKeyPress(XKeyEvent * xke)
538 if (the_locking_inset) {
539 the_locking_inset->InsetKeyPress(xke);
545 UpdatableInset::RESULT
546 InsetText::LocalDispatch(BufferView * bv,
547 int action, string const & arg)
549 no_selection = false;
550 UpdatableInset::RESULT
551 result= UpdatableInset::LocalDispatch(bv, action, arg);
552 if (result != UNDISPATCHED) {
553 resetPos(bv->painter());
558 if ((action < 0) && arg.empty())
561 if ((action != LFUN_DOWN) && (action != LFUN_UP) &&
562 (action != LFUN_DOWNSEL) && (action != LFUN_UPSEL))
564 if (the_locking_inset) {
565 result = the_locking_inset->LocalDispatch(bv, action, arg);
566 if (result == DISPATCHED_NOUPDATE)
568 else if (result == DISPATCHED) {
569 the_locking_inset->ToggleInsetCursor(bv);
570 UpdateLocal(bv, false);
571 the_locking_inset->ToggleInsetCursor(bv);
573 } else if (result == FINISHED) {
577 cursor.pos = inset_pos + 1;
578 resetPos(bv->painter());
584 the_locking_inset = 0;
592 bv->text->SetUndo(Undo::INSERT,
593 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
594 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
596 cursor.pos = selection_start;
597 par->InsertChar(cursor.pos,arg[0]);
598 SetCharFont(cursor.pos,current_font);
600 selection_start = selection_end = cursor.pos;
601 UpdateLocal(bv, true);
603 // --- Cursor Movements ---------------------------------------------
605 bv->text->FinishUndo();
606 moveRight(bv, false);
607 selection_end = cursor.pos;
608 UpdateLocal(bv, false);
611 bv->text->FinishUndo();
612 result = moveRight(bv);
613 if (hasSelection()) {
614 selection_start = selection_end = cursor.pos;
615 UpdateLocal(bv, false);
617 selection_start = selection_end = cursor.pos;
621 bv->text->FinishUndo();
623 selection_end = cursor.pos;
624 UpdateLocal(bv, false);
627 bv->text->FinishUndo();
628 result= moveLeft(bv);
629 if (hasSelection()) {
630 selection_start = selection_end = cursor.pos;
631 UpdateLocal(bv, false);
633 selection_start = selection_end = cursor.pos;
637 bv->text->FinishUndo();
639 selection_end = cursor.pos;
640 UpdateLocal(bv, false);
643 bv->text->FinishUndo();
644 result = moveDown(bv);
645 if (hasSelection()) {
646 selection_start = selection_end = cursor.pos;
647 UpdateLocal(bv, false);
649 selection_start = selection_end = cursor.pos;
653 bv->text->FinishUndo();
655 selection_end = cursor.pos;
656 UpdateLocal(bv, false);
659 bv->text->FinishUndo();
661 if (hasSelection()) {
662 selection_start = selection_end = cursor.pos;
663 UpdateLocal(bv, false);
665 selection_start = selection_end = cursor.pos;
669 if (!cursor.pos) { // || par->IsNewline(cursor.pos-1)) {
670 if (hasSelection()) {
671 selection_start = selection_end = cursor.pos;
672 UpdateLocal(bv, false);
679 bv->text->SetUndo(Undo::DELETE,
680 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
681 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
683 if (hasSelection()) {
684 LyXParagraph::size_type i = selection_start;
685 for (; i < selection_end; ++i) {
686 par->Erase(selection_start);
690 if (ret) { // we need update
691 selection_start = selection_end = cursor.pos;
692 UpdateLocal(bv, true);
693 } else if (hasSelection()) {
694 selection_start = selection_end = cursor.pos;
695 UpdateLocal(bv, false);
698 resetPos(bv->painter());
701 bv->text->SetUndo(Undo::DELETE,
702 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
703 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
705 if (cutSelection()) {
707 cursor.pos = selection_end = selection_start;
708 UpdateLocal(bv, true);
709 } else if (hasSelection()) {
710 selection_start = selection_end = cursor.pos;
711 UpdateLocal(bv, false);
713 resetPos(bv->painter());
716 bv->text->FinishUndo();
717 if (copySelection()) {
719 selection_start = selection_end = cursor.pos;
720 UpdateLocal(bv, true);
721 } else if (hasSelection()) {
722 selection_start = selection_end = cursor.pos;
723 UpdateLocal(bv, false);
728 bv->text->SetUndo(Undo::INSERT,
729 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
730 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
731 if (pasteSelection()) {
732 selection_start = selection_end = cursor.pos;
733 UpdateLocal(bv, true);
736 resetPos(bv->painter());
739 bv->text->FinishUndo();
740 for(; cursor.pos > rows[actrow].pos; --cursor.pos)
741 cursor.x -= SingleWidth(bv->painter(), par, cursor.pos);
742 cursor.x -= SingleWidth(bv->painter(), par, cursor.pos);
743 if (hasSelection()) {
744 selection_start = selection_end = cursor.pos;
745 UpdateLocal(bv, false);
747 selection_start = selection_end = cursor.pos;
749 resetPos(bv->painter());
753 bv->text->FinishUndo();
754 int checkpos = (int)rows[actrow + 1].pos;
755 if ((actrow + 2) < (int)rows.size())
757 for(; cursor.pos < checkpos; ++cursor.pos)
758 cursor.x += SingleWidth(bv->painter(), par, cursor.pos);
759 if (hasSelection()) {
760 selection_start = selection_end = cursor.pos;
761 UpdateLocal(bv, false);
763 selection_start = selection_end = cursor.pos;
766 resetPos(bv->painter());
769 InsertInset(bv, new InsetFormula);
772 InsertInset(bv, new InsetERT(buffer));
774 case LFUN_BREAKPARAGRAPH:
778 bv->text->SetUndo(Undo::INSERT,
779 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
780 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
781 par->InsertChar(cursor.pos,LyXParagraph::META_NEWLINE);
782 SetCharFont(cursor.pos,current_font);
783 UpdateLocal(bv, true);
785 selection_start = selection_end = cursor.pos;
786 resetPos(bv->painter());
789 result = UNDISPATCHED;
792 if (result != FINISHED) {
795 bv->unlockInset(this);
800 int InsetText::Latex(ostream & os, bool /*fragile*/, bool /*fp*/) const
803 int ret = par->SimpleTeXOnePar(os, texrow);
808 void InsetText::Validate(LaTeXFeatures & features) const
810 par->validate(features);
814 // Returns the width of a character at a certain spot
815 int InsetText::SingleWidth(Painter & pain, LyXParagraph * par, int pos) const
817 LyXFont font = GetDrawFont(par, pos);
818 char c = par->GetChar(pos);
820 if (IsPrintable(c)) {
821 return lyxfont::width(c, font);
822 } else if (c == LyXParagraph::META_INSET) {
823 Inset const * tmpinset = par->GetInset(pos);
825 return tmpinset->width(pain, font);
828 } else if (IsSeparatorChar(c))
830 else if (IsNewlineChar(c))
832 return lyxfont::width(c, font);
836 // Returns the width of a character at a certain spot
837 void InsetText::SingleHeight(Painter & pain, LyXParagraph * par,int pos,
838 int & asc, int & desc) const
840 LyXFont font = GetDrawFont(par, pos);
841 char c = par->GetChar(pos);
844 if (c == LyXParagraph::META_INSET) {
845 Inset const * tmpinset=par->GetInset(pos);
847 asc = tmpinset->ascent(pain, font);
848 desc = tmpinset->descent(pain, font);
851 asc = lyxfont::maxAscent(font);
852 desc = lyxfont::maxDescent(font);
858 // Gets the fully instantiated font at a given position in a paragraph
859 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
860 // The difference is that this one is used for displaying, and thus we
861 // are allowed to make cosmetic improvements. For instance make footnotes
863 // If position is -1, we get the layout font of the paragraph.
864 // If position is -2, we get the font of the manual label of the paragraph.
865 LyXFont InsetText::GetFont(LyXParagraph * par, int pos) const
867 char par_depth = par->GetDepth();
869 LyXLayout const & layout =
870 textclasslist.Style(buffer->params.textclass, par->GetLayout());
872 // We specialize the 95% common case:
873 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
876 if (layout.labeltype == LABEL_MANUAL
877 && pos < BeginningOfMainBody(par)) {
879 return par->GetFontSettings(pos).realize(layout.reslabelfont);
881 return par->GetFontSettings(pos).realize(layout.resfont);
884 // process layoutfont for pos == -1 and labelfont for pos < -1
886 return layout.resfont;
888 return layout.reslabelfont;
891 // The uncommon case need not be optimized as much
893 LyXFont layoutfont, tmpfont;
897 if (pos < BeginningOfMainBody(par)) {
899 layoutfont = layout.labelfont;
902 layoutfont = layout.font;
904 tmpfont = par->GetFontSettings(pos);
905 tmpfont.realize(layoutfont);
908 // process layoutfont for pos == -1 and labelfont for pos < -1
910 tmpfont = layout.font;
912 tmpfont = layout.labelfont;
915 // Resolve against environment font information
916 //if (par->GetDepth()){ // already in while condition
917 while (par && par_depth && !tmpfont.resolved()) {
918 par = par->DepthHook(par_depth - 1);
920 tmpfont.realize(textclasslist.Style(buffer->params.textclass,
921 par->GetLayout()).font);
922 par_depth = par->GetDepth();
925 tmpfont.realize((textclasslist.TextClass(buffer->params.textclass).
931 // the font for drawing may be different from the real font
932 LyXFont InsetText::GetDrawFont(LyXParagraph * par, int pos) const
934 return GetFont(par, pos);
938 int InsetText::BeginningOfMainBody(LyXParagraph * par) const
940 if (textclasslist.Style(buffer->params.textclass,
941 par->GetLayout()).labeltype != LABEL_MANUAL)
944 return par->BeginningOfMainBody();
948 void InsetText::GetCursorPos(int & x, int & y) const
955 int InsetText::InsetInInsetY()
957 if (!the_locking_inset)
960 return (inset_y + the_locking_inset->InsetInInsetY());
964 void InsetText::ToggleInsetCursor(BufferView * bv)
966 if (the_locking_inset) {
967 the_locking_inset->ToggleInsetCursor(bv);
971 LyXFont font = GetDrawFont(par, cursor.pos);
973 int asc = lyxfont::maxAscent(font);
974 int desc = lyxfont::maxDescent(font);
977 bv->hideLockedInsetCursor();
979 bv->showLockedInsetCursor(cursor.x, cursor.y, asc, desc);
980 cursor_visible = !cursor_visible;
984 void InsetText::ShowInsetCursor(BufferView * bv)
986 if (the_locking_inset) {
987 the_locking_inset->ShowInsetCursor(bv);
990 if (!cursor_visible) {
991 LyXFont font = GetDrawFont(par, cursor.pos);
993 int asc = lyxfont::maxAscent(font);
994 int desc = lyxfont::maxDescent(font);
995 bv->fitLockedInsetCursor(cursor.x, cursor.y, asc, desc);
996 bv->showLockedInsetCursor(cursor.x, cursor.y, asc, desc);
997 cursor_visible = true;
1002 void InsetText::HideInsetCursor(BufferView * bv)
1004 if (cursor_visible) {
1005 bv->hideLockedInsetCursor();
1006 cursor_visible = false;
1008 if (the_locking_inset)
1009 the_locking_inset->HideInsetCursor(bv);
1013 void InsetText::setPos(Painter & pain, int x, int y) const
1015 x -= drawTextXOffset;
1016 y -= drawTextYOffset;
1017 // search right X-pos x==0 -> top_x
1018 cursor.pos = actrow = 0;
1019 cursor.y = top_baseline;
1021 for(unsigned int i = 1;
1022 ((cursor.y + rows[i - 1].desc) < y) && (i < rows.size() - 1); ++i) {
1023 cursor.y = rows[i].baseline;
1024 cursor.pos = rows[i].pos;
1027 cursor.y -= top_baseline;
1032 int sw = swh = SingleWidth(pain, par,cursor.pos);
1033 if (par->GetChar(cursor.pos)!=LyXParagraph::META_INSET)
1035 int checkpos = rows[actrow + 1].pos;
1036 if ((actrow+2) < (int)rows.size())
1038 while ((cursor.pos < checkpos) && ((cursor.x + swh) < x)) {
1041 sw = swh = SingleWidth(pain, par,cursor.pos);
1042 if (par->GetChar(cursor.pos)!=LyXParagraph::META_INSET)
1048 void InsetText::resetPos(Painter & pain) const
1053 int old_pos = cursor.pos;
1055 cursor.y = top_baseline;
1057 for(unsigned int i = 0;
1058 (i < (rows.size()-1)) && (rows[i].pos <= cursor.pos);
1060 cursor.y = rows[i].baseline;
1063 cursor.y -= top_baseline;
1064 setPos(pain, 0, cursor.y);
1066 while(cursor.pos < old_pos) {
1067 cursor.x += SingleWidth(pain, par,cursor.pos);
1073 UpdatableInset::RESULT
1074 InsetText::moveRight(BufferView * bv, bool activate_inset)
1076 if (cursor.pos >= par->Last())
1078 if (activate_inset && checkAndActivateInset(bv)) {
1082 resetPos(bv->painter());
1083 real_current_font = current_font = GetFont(par, cursor.pos);
1084 return DISPATCHED_NOUPDATE;
1088 UpdatableInset::RESULT
1089 InsetText::moveLeft(BufferView * bv, bool activate_inset)
1091 if (cursor.pos <= 0)
1094 resetPos(bv->painter());
1096 if (checkAndActivateInset(bv, -1, -1))
1098 return DISPATCHED_NOUPDATE;
1102 UpdatableInset::RESULT
1103 InsetText::moveUp(BufferView * bv)
1107 cursor.y = rows[actrow - 1].baseline - top_baseline;
1108 if (cursor.x_fix < 0)
1109 cursor.x_fix = cursor.x;
1110 setPos(bv->painter(), cursor.x_fix-top_x+drawTextXOffset, cursor.y);
1111 return DISPATCHED_NOUPDATE;
1115 UpdatableInset::RESULT
1116 InsetText::moveDown(BufferView * bv)
1118 if (actrow >= int(rows.size() - 2))
1120 cursor.y = rows[actrow + 1].baseline - top_baseline;
1121 if (cursor.x_fix < 0)
1122 cursor.x_fix = cursor.x;
1123 setPos(bv->painter(), cursor.x_fix-top_x+drawTextXOffset, cursor.y);
1124 return DISPATCHED_NOUPDATE;
1128 bool InsetText::Delete()
1130 if ((par->GetChar(cursor.pos)==LyXParagraph::META_INSET) &&
1131 !par->GetInset(cursor.pos)->Deletable()) {
1134 par->Erase(cursor.pos);
1139 bool InsetText::InsertInset(BufferView * bv, Inset * inset)
1141 bv->text->SetUndo(Undo::INSERT,
1142 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
1143 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
1144 if (inset->Editable() == Inset::IS_EDITABLE) {
1145 UpdatableInset *i = (UpdatableInset *)inset;
1146 i->setOwner((UpdatableInset *)this);
1148 par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1149 par->InsertInset(cursor.pos, inset);
1150 if (hasSelection()) {
1151 selection_start = selection_end = cursor.pos;
1153 selection_start = selection_end = cursor.pos;
1155 UpdateLocal(bv, true);
1156 static_cast<UpdatableInset*>(inset)->Edit(bv, 0, 0, 0);
1161 UpdatableInset * InsetText::GetLockingInset()
1163 return the_locking_inset ? the_locking_inset->GetLockingInset() : this;
1167 void InsetText::SetFont(BufferView * bv, LyXFont const & font, bool toggleall)
1169 // if there is no selection just set the current_font
1170 if (!hasSelection()) {
1171 // Determine basis font
1173 if (cursor.pos < BeginningOfMainBody(par))
1174 layoutfont = GetFont(par, -2);
1176 layoutfont = GetFont(par, -1);
1178 // Update current font
1179 real_current_font.update(font, bv->buffer()->params.language_info,
1182 // Reduce to implicit settings
1183 current_font = real_current_font;
1184 current_font.reduce(layoutfont);
1185 // And resolve it completely
1186 real_current_font.realize(layoutfont);
1191 if (selection_start > selection_end) {
1192 s_start = selection_end;
1193 s_end = selection_start;
1195 s_start = selection_start;
1196 s_end = selection_end;
1199 while(s_start < s_end) {
1200 newfont = GetFont(par,s_start);
1201 newfont.update(font, bv->buffer()->params.language_info, toggleall);
1202 SetCharFont(s_start, newfont);
1205 UpdateLocal(bv, true);
1209 void InsetText::SetCharFont(int pos, LyXFont const & f)
1211 /* let the insets convert their font */
1214 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
1215 if (par->GetInset(pos))
1216 font = par->GetInset(pos)->ConvertFont(font);
1218 LyXLayout const & layout =
1219 textclasslist.Style(buffer->params.textclass,par->GetLayout());
1221 // Get concrete layout font to reduce against
1224 if (pos < BeginningOfMainBody(par))
1225 layoutfont = layout.labelfont;
1227 layoutfont = layout.font;
1230 layoutfont.realize((textclasslist.TextClass(buffer->params.textclass).
1233 // Now, reduce font against full layout font
1234 font.reduce(layoutfont);
1236 par->SetFont(pos, font);
1240 void InsetText::computeTextRows(Painter & pain, float x) const
1255 int width = wordAscent = wordDescent = 0;
1256 insetWidth = maxAscent = maxDescent = 0;
1261 rows.push_back(row);
1262 if (!autoBreakRows) {
1263 for(p = 0; p < par->Last(); ++p) {
1264 insetWidth += SingleWidth(pain, par, p);
1265 SingleHeight(pain, par, p, asc, desc);
1266 maxAscent = max(maxAscent, asc);
1267 maxDescent = max(maxDescent, desc);
1269 rows[0].asc = maxAscent;
1270 rows[0].desc = maxDescent;
1271 // alocate a dummy row for the endpos
1272 row.pos = par->Last();
1273 rows.push_back(row);
1277 bool is_first_word_in_row = true;
1278 int cw, lastWordWidth = 0;
1279 int maxWidth = getMaxTextWidth(pain, this, x);
1281 for(p = 0; p < par->Last(); ++p) {
1282 cw = SingleWidth(pain, par, p);
1284 lastWordWidth += cw;
1285 SingleHeight(pain, par, p, asc, desc);
1286 wordAscent = max(wordAscent, asc);
1287 wordDescent = max(wordDescent, desc);
1288 if (par->IsNewline(p)) {
1289 if (!is_first_word_in_row && (width >= maxWidth)) {
1290 // we have to split also the row above
1291 rows.back().asc = oasc;
1292 rows.back().desc = odesc;
1294 rows.push_back(row);
1296 odesc = wordDescent;
1297 insetWidth = max(insetWidth, owidth);
1298 width = lastWordWidth;
1301 rows.back().asc = wordAscent;
1302 rows.back().desc = wordDescent;
1304 rows.push_back(row);
1305 SingleHeight(pain, par, p, oasc, odesc);
1306 insetWidth = max(insetWidth, owidth);
1308 is_first_word_in_row = true;
1309 wordAscent = wordDescent = lastWordWidth = 0;
1314 if (((p + 1) < par->Last()) &&
1315 (par->GetChar(p + 1)==LyXParagraph::META_INSET))
1316 inset = par->GetInset(p + 1);
1318 inset->setOwner(const_cast<InsetText*>(this)); // is this safe?
1319 if (inset->display()) {
1320 if (!is_first_word_in_row && (width >= maxWidth)) {
1321 // we have to split also the row above
1322 rows.back().asc = oasc;
1323 rows.back().desc = odesc;
1325 rows.push_back(row);
1327 odesc = wordDescent;
1328 insetWidth = max(insetWidth, owidth);
1329 width = lastWordWidth;
1332 oasc = max(oasc, wordAscent);
1333 odesc = max(odesc, wordDescent);
1335 rows.back().asc = oasc;
1336 rows.back().desc = odesc;
1338 rows.push_back(row);
1339 SingleHeight(pain, par, p, asc, desc);
1340 rows.back().asc = asc;
1341 rows.back().desc = desc;
1342 row.pos = nwp = p + 1;
1343 rows.push_back(row);
1344 oasc = odesc = width = lastWordWidth = 0;
1345 is_first_word_in_row = true;
1346 wordAscent = wordDescent = 0;
1349 } else if (par->IsSeparator(p)) {
1350 if (width >= maxWidth) {
1351 if (is_first_word_in_row) {
1352 rows.back().asc = wordAscent;
1353 rows.back().desc = wordDescent;
1355 rows.push_back(row);
1356 oasc = odesc = width = 0;
1358 rows.back().asc = oasc;
1359 rows.back().desc = odesc;
1361 rows.push_back(row);
1363 odesc = wordDescent;
1364 insetWidth = max(insetWidth, owidth);
1365 width = lastWordWidth;
1367 wordAscent = wordDescent = lastWordWidth = 0;
1372 oasc = max(oasc, wordAscent);
1373 odesc = max(odesc, wordDescent);
1374 wordAscent = wordDescent = lastWordWidth = 0;
1376 is_first_word_in_row = false;
1379 // if we have some data in the paragraph we have ascent/descent
1381 if (width >= maxWidth) {
1383 rows.back().asc = oasc;
1384 rows.back().desc = odesc;
1385 // assign and allocate lower row
1387 rows.push_back(row);
1388 rows.back().asc = wordAscent;
1389 rows.back().desc = wordDescent;
1390 width -= lastWordWidth;
1392 // assign last row data
1393 rows.back().asc = max(oasc, wordAscent);
1394 rows.back().desc = max(odesc, wordDescent);
1397 insetWidth = max(insetWidth, width);
1398 // alocate a dummy row for the endpos
1399 row.pos = par->Last();
1400 rows.push_back(row);
1401 // calculate maxAscent/Descent
1402 maxAscent = rows[0].asc;
1403 maxDescent = rows[0].desc;
1404 for (RowList::size_type i = 1; i < rows.size() - 1; ++i) {
1405 maxDescent += rows[i].asc + rows[i].desc + interline_space;
1410 void InsetText::computeBaselines(int baseline) const
1412 rows[0].baseline = baseline;
1413 for (unsigned int i = 1; i < rows.size() - 1; i++) {
1414 rows[i].baseline = rows[i - 1].baseline + rows[i - 1].desc +
1415 rows[i].asc + interline_space;
1420 void InsetText::UpdateLocal(BufferView * bv, bool flag)
1423 computeTextRows(bv->painter(), xpos);
1424 computeBaselines(top_baseline);
1426 bv->updateInset(this, flag);
1428 resetPos(bv->painter());
1432 bool InsetText::cutSelection()
1434 if (!hasSelection())
1439 LyXParagraph * endpar = par;
1441 if (selection_start > selection_end) {
1442 start = selection_end;
1443 end = selection_start;
1445 start = selection_start;
1446 end = selection_end;
1449 return cap.cutSelection(par, &endpar, start, end,buffer->params.textclass);
1453 bool InsetText::copySelection()
1455 if (!hasSelection())
1461 if (selection_start > selection_end) {
1462 start = selection_end;
1463 end = selection_start;
1465 start = selection_start;
1466 end = selection_end;
1468 return cap.copySelection(par, par, start, end, buffer->params.textclass);
1472 bool InsetText::pasteSelection()
1476 if (cap.nrOfParagraphs() > 1) {
1477 WriteAlert(_("Impossible operation"),
1478 _("Cannot include more than one paragraph!"),
1482 LyXParagraph *endpar;
1483 LyXParagraph *actpar = par;
1485 return cap.pasteSelection(&actpar, &endpar, cursor.pos,
1486 buffer->params.textclass);
1490 bool InsetText::checkAndActivateInset(BufferView * bv, int x, int y,
1493 if (par->GetChar(cursor.pos) == LyXParagraph::META_INSET) {
1494 UpdatableInset * inset =
1495 static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
1496 LyXFont font = GetFont(par, cursor.pos);
1498 x = inset->width(bv->painter(), font);
1500 y = inset->descent(bv->painter(), font);
1501 inset_x = cursor.x - top_x + drawTextXOffset;
1502 inset_y = cursor.y + drawTextYOffset;
1503 inset->Edit(bv, x-inset_x, y-inset_y, button);
1504 if (!the_locking_inset)
1506 UpdateLocal(bv, true);
1513 int InsetText::getMaxTextWidth(Painter & pain, UpdatableInset const * inset,
1516 return getMaxWidth(pain, inset) - x;