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"
62 extern unsigned char getCurrentTextClass(Buffer *);
64 InsetText::InsetText(Buffer * buf)
66 par = new LyXParagraph();
71 InsetText::InsetText(InsetText const & ins, Buffer * buf)
75 autoBreakRows = ins.autoBreakRows;
78 void InsetText::init(Buffer * buf, InsetText const * ins)
80 the_locking_inset = 0;
82 cursor_visible = false;
84 selection_start = selection_end = 0;
88 maxAscent = maxDescent = insetWidth = 0;
89 drawTextXOffset = drawTextYOffset = 0;
90 autoBreakRows = false;
95 par = ins->par->Clone();
96 autoBreakRows = ins->autoBreakRows;
98 par->SetInsetOwner(this);
104 InsetText::~InsetText()
110 Inset * InsetText::Clone() const
112 InsetText * t = new InsetText(*this, buffer);
117 void InsetText::Write(ostream & os) const
120 WriteParagraphData(os);
124 void InsetText::WriteParagraphData(ostream & os) const
126 par->writeFile(os, buffer->params, 0, 0);
130 void InsetText::Read(LyXLex & lex)
132 string token, tmptok;
134 LyXParagraph * return_par = 0;
135 char depth = 0; // signed or unsigned?
136 LyXParagraph::footnote_flag footnoteflag = LyXParagraph::NO_FOOTNOTE;
137 LyXParagraph::footnote_kind footnotekind = LyXParagraph::FOOTNOTE;
138 LyXFont font(LyXFont::ALL_INHERIT);
141 par = new LyXParagraph;
142 par->SetInsetOwner(this);
146 token = lex.GetString();
149 if (token == "\\end_inset")
151 if (buffer->parseSingleLyXformat2Token(lex, par, return_par,
155 // the_end read this should NEVER happen
156 lex.printError("\\the_end read in inset! Error in document!");
160 if (token != "\\end_inset") {
161 lex.printError("Missing \\end_inset at this point. "
168 int InsetText::ascent(Painter & pain, LyXFont const & font) const
171 computeTextRows(pain, xpos);
177 return lyxfont::maxAscent(font);
181 int InsetText::descent(Painter & pain, LyXFont const & font) const
184 computeTextRows(pain, xpos);
190 return lyxfont::maxDescent(font);
194 int InsetText::width(Painter & pain, LyXFont const &) const
197 computeTextRows(pain, xpos);
205 void InsetText::draw(Painter & pain, LyXFont const & f,
206 int baseline, float & x) const
209 UpdatableInset::draw(pain, f, baseline, x);
212 top_baseline = baseline;
213 computeTextRows(pain, x);
214 computeBaselines(baseline);
215 if (the_locking_inset && (cursor.pos == inset_pos)) {
217 inset_x = cursor.x - top_x + drawTextXOffset;
218 inset_y = cursor.y + drawTextYOffset;
220 for(RowList::size_type r = 0; r < rows.size() - 1; ++r) {
221 drawRowSelection(pain, rows[r].pos, rows[r + 1].pos, r,
222 rows[r].baseline, x);
223 drawRowText(pain, rows[r].pos, rows[r + 1].pos, rows[r].baseline, x);
229 void InsetText::drawRowSelection(Painter & pain, int startpos, int endpos,
230 int row, int baseline, float x) const
236 if (selection_start > selection_end) {
237 s_start = selection_end;
238 s_end = selection_start;
240 s_start = selection_start;
241 s_end = selection_end;
243 if ((s_start > endpos) || (s_end < startpos))
247 int ssel_x = esel_x = int(x);
250 for(; p < endpos; ++p) {
253 if ((p >= s_start) && (p <= s_end))
255 char ch = par->GetChar(p);
256 font = GetDrawFont(par,p);
257 if (IsFloatChar(ch)) {
259 } else if (ch == LyXParagraph::META_INSET) {
260 Inset const * tmpinset = par->GetInset(p);
261 x += tmpinset->width(pain, font);
263 x += lyxfont::width(ch, font);
268 if ((p >= s_start) && (p <= s_end))
270 if (ssel_x < esel_x) {
271 pain.fillRectangle(int(ssel_x), baseline-rows[row].asc,
272 int(esel_x - ssel_x),
273 rows[row].asc + rows[row].desc,
279 void InsetText::drawRowText(Painter & pain, int startpos, int endpos,
280 int baseline, float x) const
282 Assert(endpos <= par->Last());
284 for(int p = startpos; p < endpos; ++p) {
285 char ch = par->GetChar(p);
286 LyXFont font = GetDrawFont(par,p);
287 if (IsFloatChar(ch)) {
289 } else if (par->IsNewline(p)) {
290 // Draw end-of-line marker
291 int wid = lyxfont::width('n', font);
292 int asc = lyxfont::maxAscent(font);
296 xp[0] = int(x + wid * 0.375);
297 yp[0] = int(y - 0.875 * asc * 0.75);
300 yp[1] = int(y - 0.500 * asc * 0.75);
302 xp[2] = int(x + wid * 0.375);
303 yp[2] = int(y - 0.125 * asc * 0.75);
305 pain.lines(xp, yp, 3, LColor::eolmarker);
308 yp[0] = int(y - 0.500 * asc * 0.75);
310 xp[1] = int(x + wid);
311 yp[1] = int(y - 0.500 * asc * 0.75);
313 xp[2] = int(x + wid);
314 yp[2] = int(y - asc * 0.75);
316 pain.lines(xp, yp, 3, LColor::eolmarker);
318 } else if (ch == LyXParagraph::META_INSET) {
319 Inset * tmpinset = par->GetInset(p);
321 tmpinset->draw(pain, font, baseline, x);
323 pain.text(int(x), baseline, ch, font);
324 x += lyxfont::width(ch, font);
330 char const * InsetText::EditMessage() const
332 return _("Opened Text Inset");
336 void InsetText::Edit(BufferView * bv, int x, int y, unsigned int button)
338 par->SetInsetOwner(this);
339 UpdatableInset::Edit(bv, x, y, button);
341 if (!bv->lockInset(this)) {
342 lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
345 the_locking_inset = 0;
346 selection_start = selection_end = inset_pos = inset_x = inset_y = 0;
347 // no_selection = true;
348 setPos(bv->painter(), x, y);
349 checkAndActivateInset(bv, x, y, button);
350 selection_start = selection_end = cursor.pos;
351 current_font = real_current_font = GetFont(par, cursor.pos);
352 bv->text->FinishUndo();
353 UpdateLocal(bv, true);
357 void InsetText::InsetUnlock(BufferView * bv)
359 if (the_locking_inset) {
360 the_locking_inset->InsetUnlock(bv);
361 the_locking_inset = 0;
363 lyxerr[Debug::INSETS] << "InsetText::InsetUnlock(" << this <<
365 if (hasSelection()) {
366 selection_start = selection_end = cursor.pos;
367 UpdateLocal(bv, false);
369 no_selection = false;
372 bool InsetText::LockInsetInInset(BufferView * bv, UpdatableInset * inset)
374 lyxerr[Debug::INSETS] << "InsetText::LockInsetInInset(" << inset << "): ";
377 if (inset == par->GetInset(cursor.pos)) {
378 lyxerr[Debug::INSETS] << "OK" << endl;
379 the_locking_inset = inset;
380 resetPos(bv->painter());
381 inset_x = cursor.x - top_x + drawTextXOffset;
382 inset_y = cursor.y + drawTextYOffset;
383 inset_pos = cursor.pos;
385 } else if (the_locking_inset && (the_locking_inset == inset)) {
386 if (cursor.pos == inset_pos) {
387 lyxerr[Debug::INSETS] << "OK" << endl;
388 resetPos(bv->painter());
389 inset_x = cursor.x - top_x + drawTextXOffset;
390 inset_y = cursor.y + drawTextYOffset;
392 lyxerr[Debug::INSETS] << "cursor.pos != inset_pos" << endl;
394 } else if (the_locking_inset) {
395 lyxerr[Debug::INSETS] << "MAYBE" << endl;
396 return the_locking_inset->LockInsetInInset(bv, inset);
398 lyxerr[Debug::INSETS] << "NOT OK" << endl;
402 bool InsetText::UnlockInsetInInset(BufferView * bv, UpdatableInset * inset,
405 if (!the_locking_inset)
407 if (the_locking_inset == inset) {
408 the_locking_inset->InsetUnlock(bv);
409 the_locking_inset = 0;
411 moveRight(bv, false);
414 return the_locking_inset->UnlockInsetInInset(bv, inset, lr);
418 bool InsetText::UpdateInsetInInset(BufferView * bv, Inset * inset)
420 if (!the_locking_inset)
422 if (the_locking_inset != inset)
423 return the_locking_inset->UpdateInsetInInset(bv, inset);
424 lyxerr[Debug::INSETS] << "InsetText::UpdateInsetInInset(" << inset <<
426 UpdateLocal(bv, true);
427 if (cursor.pos == inset_pos) {
428 inset_x = cursor.x - top_x + drawTextXOffset;
429 inset_y = cursor.y + drawTextYOffset;
435 void InsetText::InsetButtonPress(BufferView * bv, int x, int y, int button)
437 if (hasSelection()) {
438 selection_start = selection_end = cursor.pos;
439 UpdateLocal(bv, false);
441 no_selection = false;
442 setPos(bv->painter(), x, y);
444 if (the_locking_inset) {
447 if (par->GetChar(cursor.pos) == LyXParagraph::META_INSET)
448 inset = static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
449 if (the_locking_inset == inset) {
450 the_locking_inset->InsetButtonPress(bv,x-inset_x,y-inset_y,button);
453 // otherwise unlock the_locking_inset and lock the new inset
454 the_locking_inset->InsetUnlock(bv);
455 inset_x = cursor.x - top_x + drawTextXOffset;
456 inset_y = cursor.y + drawTextYOffset;
457 inset->InsetButtonPress(bv, x-inset_x, y-inset_y, button);
458 inset->Edit(bv, x - inset_x, y - inset_y, button);
459 UpdateLocal(bv, true);
462 // otherwise only unlock the_locking_inset
463 the_locking_inset->InsetUnlock(bv);
464 the_locking_inset = 0;
466 if (bv->the_locking_inset) {
467 if ((par->GetChar(cursor.pos) == LyXParagraph::META_INSET) &&
468 par->GetInset(cursor.pos) &&
469 (par->GetInset(cursor.pos)->Editable() == Inset::HIGHLY_EDITABLE)) {
470 UpdatableInset *inset =
471 static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
472 inset_x = cursor.x - top_x + drawTextXOffset;
473 inset_y = cursor.y + drawTextYOffset;
474 inset->InsetButtonPress(bv, x-inset_x, y-inset_y, button);
475 inset->Edit(bv, x-inset_x, y-inset_y, 0);
476 UpdateLocal(bv, true);
479 selection_start = selection_end = cursor.pos;
483 void InsetText::InsetButtonRelease(BufferView * bv, int x, int y, int button)
485 UpdatableInset * inset = 0;
487 if (the_locking_inset) {
488 the_locking_inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
490 if (par->GetChar(cursor.pos) == LyXParagraph::META_INSET) {
491 inset = static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
492 if (inset->Editable()==Inset::HIGHLY_EDITABLE) {
493 inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
495 inset_x = cursor.x - top_x + drawTextXOffset;
496 inset_y = cursor.y + drawTextYOffset;
497 inset->InsetButtonRelease(bv, x-inset_x, y-inset_y,button);
498 inset->Edit(bv, x-inset_x, y-inset_y, button);
502 no_selection = false;
506 void InsetText::InsetMotionNotify(BufferView * bv, int x, int y, int state)
508 if (the_locking_inset) {
509 the_locking_inset->InsetMotionNotify(bv, x - inset_x,
514 int old = selection_end;
516 setPos(bv->painter(), x, y);
517 selection_end = cursor.pos;
518 if (old != selection_end)
519 UpdateLocal(bv, false);
522 no_selection = false;
526 void InsetText::InsetKeyPress(XKeyEvent * xke)
528 if (the_locking_inset) {
529 the_locking_inset->InsetKeyPress(xke);
535 UpdatableInset::RESULT
536 InsetText::LocalDispatch(BufferView * bv,
537 int action, string const & arg)
539 no_selection = false;
540 UpdatableInset::RESULT
541 result= UpdatableInset::LocalDispatch(bv, action, arg);
542 if (result != UNDISPATCHED) {
543 resetPos(bv->painter());
548 if ((action < 0) && arg.empty())
551 if ((action != LFUN_DOWN) && (action != LFUN_UP) &&
552 (action != LFUN_DOWNSEL) && (action != LFUN_UPSEL))
554 if (the_locking_inset) {
555 result = the_locking_inset->LocalDispatch(bv, action, arg);
556 if (result == DISPATCHED_NOUPDATE)
558 else if (result == DISPATCHED) {
559 the_locking_inset->ToggleInsetCursor(bv);
560 UpdateLocal(bv, false);
561 the_locking_inset->ToggleInsetCursor(bv);
563 } else if (result == FINISHED) {
567 cursor.pos = inset_pos + 1;
568 resetPos(bv->painter());
574 the_locking_inset = 0;
582 bv->text->SetUndo(Undo::INSERT,
583 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
584 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
586 cursor.pos = selection_start;
587 par->InsertChar(cursor.pos,arg[0]);
588 SetCharFont(cursor.pos,current_font);
590 selection_start = selection_end = cursor.pos;
591 UpdateLocal(bv, true);
593 // --- Cursor Movements ---------------------------------------------
595 bv->text->FinishUndo();
596 moveRight(bv, false);
597 selection_end = cursor.pos;
598 UpdateLocal(bv, false);
601 bv->text->FinishUndo();
602 result = moveRight(bv);
603 if (hasSelection()) {
604 selection_start = selection_end = cursor.pos;
605 UpdateLocal(bv, false);
607 selection_start = selection_end = cursor.pos;
611 bv->text->FinishUndo();
613 selection_end = cursor.pos;
614 UpdateLocal(bv, false);
617 bv->text->FinishUndo();
618 result= moveLeft(bv);
619 if (hasSelection()) {
620 selection_start = selection_end = cursor.pos;
621 UpdateLocal(bv, false);
623 selection_start = selection_end = cursor.pos;
627 bv->text->FinishUndo();
629 selection_end = cursor.pos;
630 UpdateLocal(bv, false);
633 bv->text->FinishUndo();
634 result = moveDown(bv);
635 if (hasSelection()) {
636 selection_start = selection_end = cursor.pos;
637 UpdateLocal(bv, false);
639 selection_start = selection_end = cursor.pos;
643 bv->text->FinishUndo();
645 selection_end = cursor.pos;
646 UpdateLocal(bv, false);
649 bv->text->FinishUndo();
651 if (hasSelection()) {
652 selection_start = selection_end = cursor.pos;
653 UpdateLocal(bv, false);
655 selection_start = selection_end = cursor.pos;
659 if (!cursor.pos) { // || par->IsNewline(cursor.pos-1)) {
660 if (hasSelection()) {
661 selection_start = selection_end = cursor.pos;
662 UpdateLocal(bv, false);
669 bv->text->SetUndo(Undo::DELETE,
670 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
671 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
673 if (hasSelection()) {
674 LyXParagraph::size_type i = selection_start;
675 for (; i < selection_end; ++i) {
676 par->Erase(selection_start);
680 if (ret) { // we need update
681 selection_start = selection_end = cursor.pos;
682 UpdateLocal(bv, true);
683 } else if (hasSelection()) {
684 selection_start = selection_end = cursor.pos;
685 UpdateLocal(bv, false);
688 resetPos(bv->painter());
691 bv->text->SetUndo(Undo::DELETE,
692 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
693 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
695 if (cutSelection()) {
697 cursor.pos = selection_end = selection_start;
698 UpdateLocal(bv, true);
699 } else if (hasSelection()) {
700 selection_start = selection_end = cursor.pos;
701 UpdateLocal(bv, false);
703 resetPos(bv->painter());
706 bv->text->FinishUndo();
707 if (copySelection()) {
709 selection_start = selection_end = cursor.pos;
710 UpdateLocal(bv, true);
711 } else if (hasSelection()) {
712 selection_start = selection_end = cursor.pos;
713 UpdateLocal(bv, false);
718 bv->text->SetUndo(Undo::INSERT,
719 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
720 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
721 if (pasteSelection()) {
722 selection_start = selection_end = cursor.pos;
723 UpdateLocal(bv, true);
726 resetPos(bv->painter());
729 bv->text->FinishUndo();
730 for(; cursor.pos > rows[actrow].pos; --cursor.pos)
731 cursor.x -= SingleWidth(bv->painter(), par, cursor.pos);
732 cursor.x -= SingleWidth(bv->painter(), par, cursor.pos);
733 if (hasSelection()) {
734 selection_start = selection_end = cursor.pos;
735 UpdateLocal(bv, false);
737 selection_start = selection_end = cursor.pos;
739 resetPos(bv->painter());
743 bv->text->FinishUndo();
744 int checkpos = (int)rows[actrow + 1].pos;
745 if ((actrow + 2) < (int)rows.size())
747 for(; cursor.pos < checkpos; ++cursor.pos)
748 cursor.x += SingleWidth(bv->painter(), par, cursor.pos);
749 if (hasSelection()) {
750 selection_start = selection_end = cursor.pos;
751 UpdateLocal(bv, false);
753 selection_start = selection_end = cursor.pos;
756 resetPos(bv->painter());
759 InsertInset(bv, new InsetFormula);
762 InsertInset(bv, new InsetERT(buffer));
764 case LFUN_BREAKPARAGRAPH:
768 bv->text->SetUndo(Undo::INSERT,
769 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
770 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
771 par->InsertChar(cursor.pos,LyXParagraph::META_NEWLINE);
772 SetCharFont(cursor.pos,current_font);
773 UpdateLocal(bv, true);
775 selection_start = selection_end = cursor.pos;
776 resetPos(bv->painter());
779 result = UNDISPATCHED;
782 if (result != FINISHED) {
785 bv->unlockInset(this);
790 int InsetText::Latex(ostream & os, bool /*fragile*/, bool) const
793 int ret = par->SimpleTeXOnePar(os, texrow);
798 void InsetText::Validate(LaTeXFeatures & features) const
800 par->validate(features);
804 // Returns the width of a character at a certain spot
805 int InsetText::SingleWidth(Painter & pain, LyXParagraph * par, int pos) const
807 LyXFont font = GetDrawFont(par, pos);
808 char c = par->GetChar(pos);
810 if (IsPrintable(c)) {
811 return lyxfont::width(c, font);
812 } else if (c == LyXParagraph::META_INSET) {
813 Inset const * tmpinset = par->GetInset(pos);
815 return tmpinset->width(pain, font);
818 } else if (IsSeparatorChar(c))
820 else if (IsNewlineChar(c))
822 return lyxfont::width(c, font);
826 // Returns the width of a character at a certain spot
827 void InsetText::SingleHeight(Painter & pain, LyXParagraph * par,int pos,
828 int & asc, int & desc) const
830 LyXFont font = GetDrawFont(par, pos);
831 char c = par->GetChar(pos);
834 if (c == LyXParagraph::META_INSET) {
835 Inset const * tmpinset=par->GetInset(pos);
837 asc = tmpinset->ascent(pain, font);
838 desc = tmpinset->descent(pain, font);
841 asc = lyxfont::maxAscent(font);
842 desc = lyxfont::maxDescent(font);
848 // Gets the fully instantiated font at a given position in a paragraph
849 // Basically the same routine as LyXParagraph::getFont() in paragraph.C.
850 // The difference is that this one is used for displaying, and thus we
851 // are allowed to make cosmetic improvements. For instance make footnotes
853 // If position is -1, we get the layout font of the paragraph.
854 // If position is -2, we get the font of the manual label of the paragraph.
855 LyXFont InsetText::GetFont(LyXParagraph * par, int pos) const
857 char par_depth = par->GetDepth();
859 LyXLayout const & layout =
860 textclasslist.Style(buffer->params.textclass, par->GetLayout());
862 // We specialize the 95% common case:
863 if (par->footnoteflag == LyXParagraph::NO_FOOTNOTE && !par_depth) {
866 if (layout.labeltype == LABEL_MANUAL
867 && pos < BeginningOfMainBody(par)) {
869 return par->GetFontSettings(pos).realize(layout.reslabelfont);
871 return par->GetFontSettings(pos).realize(layout.resfont);
874 // process layoutfont for pos == -1 and labelfont for pos < -1
876 return layout.resfont;
878 return layout.reslabelfont;
881 // The uncommon case need not be optimized as much
883 LyXFont layoutfont, tmpfont;
887 if (pos < BeginningOfMainBody(par)) {
889 layoutfont = layout.labelfont;
892 layoutfont = layout.font;
894 tmpfont = par->GetFontSettings(pos);
895 tmpfont.realize(layoutfont);
898 // process layoutfont for pos == -1 and labelfont for pos < -1
900 tmpfont = layout.font;
902 tmpfont = layout.labelfont;
905 // Resolve against environment font information
906 //if (par->GetDepth()){ // already in while condition
907 while (par && par_depth && !tmpfont.resolved()) {
908 par = par->DepthHook(par_depth - 1);
910 tmpfont.realize(textclasslist.Style(buffer->params.textclass,
911 par->GetLayout()).font);
912 par_depth = par->GetDepth();
915 tmpfont.realize((textclasslist.TextClass(buffer->params.textclass).
920 // the font for drawing may be different from the real font
921 LyXFont InsetText::GetDrawFont(LyXParagraph * par, int pos) const
923 return GetFont(par, pos);
926 int InsetText::BeginningOfMainBody(LyXParagraph * par) const
928 if (textclasslist.Style(buffer->params.textclass,
929 par->GetLayout()).labeltype != LABEL_MANUAL)
932 return par->BeginningOfMainBody();
936 void InsetText::GetCursorPos(int & x, int & y) const
943 int InsetText::InsetInInsetY()
945 if (!the_locking_inset)
948 return (inset_y + the_locking_inset->InsetInInsetY());
952 void InsetText::ToggleInsetCursor(BufferView * bv)
954 if (the_locking_inset) {
955 the_locking_inset->ToggleInsetCursor(bv);
959 LyXFont font = GetDrawFont(par, cursor.pos);
961 int asc = lyxfont::maxAscent(font);
962 int desc = lyxfont::maxDescent(font);
965 bv->hideLockedInsetCursor();
967 bv->showLockedInsetCursor(cursor.x, cursor.y, asc, desc);
968 cursor_visible = !cursor_visible;
972 void InsetText::ShowInsetCursor(BufferView * bv)
974 if (the_locking_inset) {
975 the_locking_inset->ShowInsetCursor(bv);
978 if (!cursor_visible) {
979 LyXFont font = GetDrawFont(par, cursor.pos);
981 int asc = lyxfont::maxAscent(font);
982 int desc = lyxfont::maxDescent(font);
983 bv->fitLockedInsetCursor(cursor.x, cursor.y, asc, desc);
984 bv->showLockedInsetCursor(cursor.x, cursor.y, asc, desc);
985 cursor_visible = true;
990 void InsetText::HideInsetCursor(BufferView * bv)
992 if (cursor_visible) {
993 bv->hideLockedInsetCursor();
994 cursor_visible = false;
996 if (the_locking_inset)
997 the_locking_inset->HideInsetCursor(bv);
1001 void InsetText::setPos(Painter & pain, int x, int y) const
1003 x -= drawTextXOffset;
1004 y -= drawTextYOffset;
1005 // search right X-pos x==0 -> top_x
1006 cursor.pos = actrow = 0;
1007 cursor.y = top_baseline;
1009 for(unsigned int i = 1;
1010 ((cursor.y + rows[i - 1].desc) < y) && (i < rows.size() - 1); ++i) {
1011 cursor.y = rows[i].baseline;
1012 cursor.pos = rows[i].pos;
1015 cursor.y -= top_baseline;
1020 int sw = swh = SingleWidth(pain, par,cursor.pos);
1021 if (par->GetChar(cursor.pos)!=LyXParagraph::META_INSET)
1023 int checkpos = rows[actrow + 1].pos;
1024 if ((actrow+2) < (int)rows.size())
1026 while ((cursor.pos < checkpos) && ((cursor.x + swh) < x)) {
1029 sw = swh = SingleWidth(pain, par,cursor.pos);
1030 if (par->GetChar(cursor.pos)!=LyXParagraph::META_INSET)
1036 void InsetText::resetPos(Painter & pain) const
1041 int old_pos = cursor.pos;
1043 cursor.y = top_baseline;
1045 for(unsigned int i = 0; (i < (rows.size()-1)) && (rows[i].pos <= cursor.pos);
1047 cursor.y = rows[i].baseline;
1050 cursor.y -= top_baseline;
1051 setPos(pain, 0, cursor.y);
1053 while(cursor.pos < old_pos) {
1054 cursor.x += SingleWidth(pain, par,cursor.pos);
1060 UpdatableInset::RESULT
1061 InsetText::moveRight(BufferView * bv, bool activate_inset)
1063 if (cursor.pos >= par->Last())
1065 if (activate_inset && checkAndActivateInset(bv)) {
1069 resetPos(bv->painter());
1070 real_current_font = current_font = GetFont(par, cursor.pos);
1071 return DISPATCHED_NOUPDATE;
1075 UpdatableInset::RESULT
1076 InsetText::moveLeft(BufferView * bv, bool activate_inset)
1078 if (cursor.pos <= 0)
1081 resetPos(bv->painter());
1083 if (checkAndActivateInset(bv, -1, -1))
1085 return DISPATCHED_NOUPDATE;
1089 UpdatableInset::RESULT
1090 InsetText::moveUp(BufferView * bv)
1094 cursor.y = rows[actrow - 1].baseline - top_baseline;
1095 if (cursor.x_fix < 0)
1096 cursor.x_fix = cursor.x;
1097 setPos(bv->painter(), cursor.x_fix-top_x+drawTextXOffset, cursor.y);
1098 return DISPATCHED_NOUPDATE;
1102 UpdatableInset::RESULT
1103 InsetText::moveDown(BufferView * bv)
1105 if (actrow >= int(rows.size() - 2))
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 bool InsetText::Delete()
1117 if ((par->GetChar(cursor.pos)==LyXParagraph::META_INSET) &&
1118 !par->GetInset(cursor.pos)->Deletable()) {
1121 par->Erase(cursor.pos);
1126 bool InsetText::InsertInset(BufferView * bv, Inset * inset)
1128 bv->text->SetUndo(Undo::INSERT,
1129 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->previous,
1130 bv->text->cursor.par->ParFromPos(bv->text->cursor.pos)->next);
1131 if (inset->Editable() == Inset::IS_EDITABLE) {
1132 UpdatableInset *i = (UpdatableInset *)inset;
1133 i->setOwner((UpdatableInset *)this);
1135 par->InsertChar(cursor.pos, LyXParagraph::META_INSET);
1136 par->InsertInset(cursor.pos, inset);
1137 if (hasSelection()) {
1138 selection_start = selection_end = cursor.pos;
1140 selection_start = selection_end = cursor.pos;
1142 UpdateLocal(bv, true);
1143 static_cast<UpdatableInset*>(inset)->Edit(bv, 0, 0, 0);
1148 UpdatableInset * InsetText::GetLockingInset()
1150 return the_locking_inset ? the_locking_inset->GetLockingInset() : this;
1154 void InsetText::SetFont(BufferView * bv, LyXFont const & font, bool toggleall)
1156 // if there is no selection just set the current_font
1157 if (!hasSelection()) {
1158 // Determine basis font
1160 if (cursor.pos < BeginningOfMainBody(par))
1161 layoutfont = GetFont(par, -2);
1163 layoutfont = GetFont(par, -1);
1165 // Update current font
1166 real_current_font.update(font, bv->buffer()->params.language_info,
1169 // Reduce to implicit settings
1170 current_font = real_current_font;
1171 current_font.reduce(layoutfont);
1172 // And resolve it completely
1173 real_current_font.realize(layoutfont);
1178 if (selection_start > selection_end) {
1179 s_start = selection_end;
1180 s_end = selection_start;
1182 s_start = selection_start;
1183 s_end = selection_end;
1186 while(s_start < s_end) {
1187 newfont = GetFont(par,s_start);
1188 newfont.update(font, bv->buffer()->params.language_info, toggleall);
1189 SetCharFont(s_start, newfont);
1192 UpdateLocal(bv, true);
1196 void InsetText::SetCharFont(int pos, LyXFont const & f)
1198 /* let the insets convert their font */
1201 if (par->GetChar(pos) == LyXParagraph::META_INSET) {
1202 if (par->GetInset(pos))
1203 font = par->GetInset(pos)->ConvertFont(font);
1205 LyXLayout const & layout =
1206 textclasslist.Style(buffer->params.textclass,par->GetLayout());
1208 // Get concrete layout font to reduce against
1211 if (pos < BeginningOfMainBody(par))
1212 layoutfont = layout.labelfont;
1214 layoutfont = layout.font;
1217 layoutfont.realize((textclasslist.TextClass(buffer->params.textclass).
1220 // Now, reduce font against full layout font
1221 font.reduce(layoutfont);
1223 par->SetFont(pos, font);
1227 void InsetText::computeTextRows(Painter & pain, float x) const
1242 int width = wordAscent = wordDescent = 0;
1243 insetWidth = maxAscent = maxDescent = 0;
1248 rows.push_back(row);
1249 if (!autoBreakRows) {
1250 for(p = 0; p < par->Last(); ++p) {
1251 insetWidth += SingleWidth(pain, par, p);
1252 SingleHeight(pain, par, p, asc, desc);
1253 maxAscent = max(maxAscent, asc);
1254 maxDescent = max(maxDescent, desc);
1256 rows[0].asc = maxAscent;
1257 rows[0].desc = maxDescent;
1258 // alocate a dummy row for the endpos
1259 row.pos = par->Last();
1260 rows.push_back(row);
1264 bool is_first_word_in_row = true;
1265 int cw, lastWordWidth = 0;
1266 int maxWidth = getMaxTextWidth(pain, this, x);
1268 for(p = 0; p < par->Last(); ++p) {
1269 cw = SingleWidth(pain, par, p);
1271 lastWordWidth += cw;
1272 SingleHeight(pain, par, p, asc, desc);
1273 wordAscent = max(wordAscent, asc);
1274 wordDescent = max(wordDescent, desc);
1275 if (par->IsNewline(p)) {
1276 if (!is_first_word_in_row && (width >= maxWidth)) {
1277 // we have to split also the row above
1278 rows.back().asc = oasc;
1279 rows.back().desc = odesc;
1281 rows.push_back(row);
1283 odesc = wordDescent;
1284 insetWidth = max(insetWidth, owidth);
1285 width = lastWordWidth;
1288 rows.back().asc = wordAscent;
1289 rows.back().desc = wordDescent;
1290 row.pos = ++p; // +1;
1291 rows.push_back(row);
1292 SingleHeight(pain, par, p, oasc, odesc);
1293 insetWidth = max(insetWidth, owidth);
1295 is_first_word_in_row = true;
1296 wordAscent = wordDescent = lastWordWidth = 0;
1300 Inset const * inset = 0;
1301 if (((p + 1) < par->Last()) &&
1302 (par->GetChar(p + 1)==LyXParagraph::META_INSET))
1303 inset = par->GetInset(p + 1);
1304 if (inset && inset->display()) {
1305 if (!is_first_word_in_row && (width >= maxWidth)) {
1306 // we have to split also the row above
1307 rows.back().asc = oasc;
1308 rows.back().desc = odesc;
1310 rows.push_back(row);
1312 odesc = wordDescent;
1313 insetWidth = max(insetWidth, owidth);
1314 width = lastWordWidth;
1317 oasc = max(oasc, wordAscent);
1318 odesc = max(odesc, wordDescent);
1320 rows.back().asc = oasc;
1321 rows.back().desc = odesc;
1323 rows.push_back(row);
1324 SingleHeight(pain, par, p, asc, desc);
1325 rows.back().asc = asc;
1326 rows.back().desc = desc;
1327 row.pos = nwp = p + 1;
1328 rows.push_back(row);
1329 oasc = odesc = width = lastWordWidth = 0;
1330 is_first_word_in_row = true;
1331 wordAscent = wordDescent = 0;
1333 } else if (par->IsSeparator(p)) {
1334 if (width >= maxWidth) {
1335 if (is_first_word_in_row) {
1336 rows.back().asc = wordAscent;
1337 rows.back().desc = wordDescent;
1339 rows.push_back(row);
1340 oasc = odesc = width = 0;
1342 rows.back().asc = oasc;
1343 rows.back().desc = odesc;
1345 rows.push_back(row);
1347 odesc = wordDescent;
1348 insetWidth = max(insetWidth, owidth);
1349 width = lastWordWidth;
1351 wordAscent = wordDescent = lastWordWidth = 0;
1356 oasc = max(oasc, wordAscent);
1357 odesc = max(odesc, wordDescent);
1358 wordAscent = wordDescent = lastWordWidth = 0;
1360 is_first_word_in_row = false;
1363 // if we have some data in the paragraph we have ascent/descent
1365 if (width >= maxWidth) {
1367 rows.back().asc = oasc;
1368 rows.back().desc = odesc;
1369 // assign and allocate lower row
1371 rows.push_back(row);
1372 rows.back().asc = wordAscent;
1373 rows.back().desc = wordDescent;
1374 width -= lastWordWidth;
1376 // assign last row data
1377 rows.back().asc = max(oasc, wordAscent);
1378 rows.back().desc = max(odesc, wordDescent);
1381 insetWidth = max(insetWidth, width);
1382 // alocate a dummy row for the endpos
1383 row.pos = par->Last();
1384 rows.push_back(row);
1385 // calculate maxAscent/Descent
1386 maxAscent = rows[0].asc;
1387 maxDescent = rows[0].desc;
1388 for (RowList::size_type i = 1; i < rows.size() - 1; ++i) {
1389 maxDescent += rows[i].asc + rows[i].desc + interline_space;
1394 void InsetText::computeBaselines(int baseline) const
1396 rows[0].baseline = baseline;
1397 for (unsigned int i = 1; i < rows.size() - 1; i++) {
1398 rows[i].baseline = rows[i - 1].baseline + rows[i - 1].desc +
1399 rows[i].asc + interline_space;
1403 void InsetText::UpdateLocal(BufferView *bv, bool flag)
1407 computeTextRows(bv->painter(), xpos);
1408 computeBaselines(top_baseline);
1410 bv->updateInset(this, flag);
1412 resetPos(bv->painter());
1415 bv->updateInset(this, flag);
1419 bool InsetText::cutSelection()
1421 if (!hasSelection())
1426 LyXParagraph *endpar = par;
1428 return cap.cutSelection(par, &endpar, selection_start, selection_end,
1429 buffer->params.textclass);
1432 bool InsetText::copySelection()
1434 if (!hasSelection())
1439 return cap.copySelection(par, par, selection_start, selection_end,
1440 buffer->params.textclass);
1443 bool InsetText::pasteSelection()
1447 if (cap.nrOfParagraphs() > 1) {
1448 WriteAlert(_("Impossible operation"),
1449 _("Cannot include more than one paragraph!"),
1453 LyXParagraph *endpar;
1454 LyXParagraph *actpar = par;
1456 return cap.pasteSelection(&actpar, &endpar, cursor.pos,
1457 buffer->params.textclass);
1460 bool InsetText::checkAndActivateInset(BufferView * bv, int x, int y,
1463 if (par->GetChar(cursor.pos) == LyXParagraph::META_INSET) {
1464 UpdatableInset * inset =
1465 static_cast<UpdatableInset*>(par->GetInset(cursor.pos));
1466 LyXFont font = GetFont(par, cursor.pos);
1468 x = inset->width(bv->painter(), font);
1470 y = inset->descent(bv->painter(), font);
1471 inset_x = cursor.x - top_x + drawTextXOffset;
1472 inset_y = cursor.y + drawTextYOffset;
1473 inset->Edit(bv, x-inset_x, y-inset_y, button);
1474 if (!the_locking_inset)
1476 UpdateLocal(bv, true);
1482 int InsetText::getMaxTextWidth(Painter & pain, UpdatableInset const * inset,
1485 return getMaxWidth(pain, inset) - x;