1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "frontends/LyXView.h"
21 #include "undo_funcs.h"
23 #include "bufferparams.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
32 #include "FloatList.h"
34 #include "ParagraphParameters.h"
36 #include "insets/inseterror.h"
37 #include "insets/insetbib.h"
38 #include "insets/insetspecialchar.h"
39 #include "insets/insettext.h"
40 #include "insets/insetfloat.h"
42 #include "support/LAssert.h"
43 #include "support/textutils.h"
44 #include "support/lstrings.h"
55 LyXText::LyXText(BufferView * bv)
56 : number_of_rows(0), height(0), width(0), first_y(0),
57 bv_owner(bv), inset_owner(0), the_locking_inset(0),
58 need_break_row(0), refresh_y(0), refresh_row(0),
59 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
63 LyXText::LyXText(InsetText * inset)
64 : number_of_rows(0), height(0), width(0), first_y(0),
65 bv_owner(0), inset_owner(inset), the_locking_inset(0),
66 need_break_row(0), refresh_y(0), refresh_row(0),
67 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
71 void LyXText::init(BufferView * bview, bool reinit)
74 // Delete all rows, this does not touch the paragraphs!
75 Row * tmprow = firstrow;
77 tmprow = firstrow->next();
86 copylayouttype.erase();
87 number_of_rows = first_y = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
96 insertParagraph(bview, par, lastrow);
99 setCursorIntern(bview, firstrow->par(), 0);
100 selection.cursor = cursor;
106 // Delete all rows, this does not touch the paragraphs!
107 Row * tmprow = firstrow;
109 tmprow = firstrow->next();
118 LyXFont const realizeFont(LyXFont const & font,
122 LyXTextClass const & tclass = buf->params.getLyXTextClass();
123 LyXFont tmpfont(font);
124 Paragraph::depth_type par_depth = par->getDepth();
126 // Resolve against environment font information
127 while (par && par_depth && !tmpfont.resolved()) {
128 par = par->outerHook();
130 #ifndef INHERIT_LANGUAGE
131 tmpfont.realize(par->layout()->font);
133 tmpfont.realize(tclass[par->layout()]->font,
134 buf->params.language);
136 par_depth = par->getDepth();
140 #ifndef INHERIT_LANGUAGE
141 tmpfont.realize(tclass.defaultfont());
143 tmpfont.realize(tclass.defaultfont(), buf->params.language);
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
162 lyx::Assert(pos >= 0);
164 LyXLayout_ptr const & layout = par->layout();
166 Paragraph::depth_type par_depth = par->getDepth();
167 // We specialize the 95% common case:
169 if (layout->labeltype == LABEL_MANUAL
170 && pos < beginningOfMainBody(buf, par)) {
172 LyXFont f = par->getFontSettings(buf->params, pos);
174 par->inInset()->getDrawFont(f);
175 #ifndef INHERIT_LANGUAGE
176 return f.realize(layout->reslabelfont);
178 return f.realize(layout.reslabelfont, buf->params.language);
181 LyXFont f = par->getFontSettings(buf->params, pos);
183 par->inInset()->getDrawFont(f);
184 #ifndef INHERIT_LANGUAGE
185 return f.realize(layout->resfont);
187 return f.realize(layout.resfont, buf->params.language);
192 // The uncommon case need not be optimized as much
196 if (pos < beginningOfMainBody(buf, par)) {
198 layoutfont = layout->labelfont;
201 layoutfont = layout->font;
204 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
205 #ifndef INHERIT_LANGUAGE
206 tmpfont.realize(layoutfont);
208 tmpfont.realize(layoutfont, buf->params.language);
211 par->inInset()->getDrawFont(tmpfont);
213 return realizeFont(tmpfont, buf, par);
217 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
219 LyXLayout_ptr const & layout = par->layout();
221 Paragraph::depth_type par_depth = par->getDepth();
224 return layout->resfont;
227 return realizeFont(layout->font, buf, par);
231 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
233 LyXLayout_ptr const & layout = par->layout();
235 Paragraph::depth_type par_depth = par->getDepth();
238 return layout->reslabelfont;
241 return realizeFont(layout->labelfont, buf, par);
245 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
246 pos_type pos, LyXFont const & fnt,
249 Buffer const * buf = bv->buffer();
250 LyXFont font = getFont(buf, par, pos);
251 font.update(fnt, buf->params.language, toggleall);
252 // Let the insets convert their font
253 if (par->isInset(pos)) {
254 Inset * inset = par->getInset(pos);
255 if (isEditableInset(inset)) {
256 UpdatableInset * uinset =
257 static_cast<UpdatableInset *>(inset);
258 uinset->setFont(bv, fnt, toggleall, true);
262 // Plug thru to version below:
263 setCharFont(buf, par, pos, font);
267 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
268 pos_type pos, LyXFont const & fnt)
272 LyXTextClass const & tclass = buf->params.getLyXTextClass();
273 LyXLayout_ptr const & layout = par->layout();
275 // Get concrete layout font to reduce against
278 if (pos < beginningOfMainBody(buf, par))
279 layoutfont = layout->labelfont;
281 layoutfont = layout->font;
283 // Realize against environment font information
284 if (par->getDepth()) {
285 Paragraph * tp = par;
286 while (!layoutfont.resolved() && tp && tp->getDepth()) {
287 tp = tp->outerHook();
289 #ifndef INHERIT_LANGUAGE
290 layoutfont.realize(tp->layout()->font);
292 layoutfont.realize(tclass[tp->layout()].font,
293 buf->params.language);
298 #ifndef INHERIT_LANGUAGE
299 layoutfont.realize(tclass.defaultfont());
301 layoutfont.realize(tclass.defaultfont(), buf->params.language);
304 // Now, reduce font against full layout font
305 font.reduce(layoutfont);
307 par->setFont(pos, font);
311 // inserts a new row behind the specified row, increments
312 // the touched counters
313 void LyXText::insertRow(Row * row, Paragraph * par,
316 Row * tmprow = new Row;
319 tmprow->next(firstrow);
322 tmprow->previous(row);
323 tmprow->next(row->next());
328 tmprow->next()->previous(tmprow);
330 if (tmprow->previous())
331 tmprow->previous()->next(tmprow);
343 // removes the row and reset the touched counters
344 void LyXText::removeRow(Row * row) const
346 Row * row_prev = row->previous();
348 row->next()->previous(row_prev);
350 firstrow = row->next();
351 // lyx::Assert(firstrow);
353 row_prev->next(row->next());
355 if (row == lastrow) {
356 lyx::Assert(!row->next());
359 if (refresh_row == row) {
360 refresh_row = row_prev ? row_prev : row->next();
361 // what about refresh_y, refresh_height
364 height -= row->height(); // the text becomes smaller
367 --number_of_rows; // one row less
371 // remove all following rows of the paragraph of the specified row.
372 void LyXText::removeParagraph(Row * row) const
374 Paragraph * tmppar = row->par();
378 while (row && row->par() == tmppar) {
379 tmprow = row->next();
386 // insert the specified paragraph behind the specified row
387 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
390 insertRow(row, par, 0); /* insert a new row, starting
393 setCounter(bview->buffer(), par); // set the counters
395 // and now append the whole paragraph behind the new row
398 appendParagraph(bview, firstrow);
400 row->next()->height(0);
401 appendParagraph(bview, row->next());
406 Inset * LyXText::getInset() const
409 if (cursor.pos() == 0 && cursor.par()->bibkey) {
410 inset = cursor.par()->bibkey;
411 } else if (cursor.pos() < cursor.par()->size()
412 && cursor.par()->isInset(cursor.pos())) {
413 inset = cursor.par()->getInset(cursor.pos());
419 void LyXText::toggleInset(BufferView * bview)
421 Inset * inset = getInset();
422 // is there an editable inset at cursor position?
423 if (!isEditableInset(inset)) {
424 // No, try to see if we are inside a collapsable inset
425 if (inset_owner && inset_owner->owner()
426 && inset_owner->owner()->isOpen()) {
427 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
428 inset_owner->owner()->close(bview);
429 bview->getLyXText()->cursorRight(bview);
433 //bview->owner()->message(inset->editMessage());
435 // do we want to keep this?? (JMarc)
436 if (!isHighlyEditableInset(inset))
437 setCursorParUndo(bview);
439 if (inset->isOpen()) {
445 inset->open(bview, !inset->isOpen());
450 /* used in setlayout */
451 // Asger is not sure we want to do this...
452 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
455 LyXLayout_ptr const & layout = par->layout();
458 for (pos_type pos = 0; pos < par->size(); ++pos) {
459 if (pos < beginningOfMainBody(buf, par))
460 layoutfont = layout->labelfont;
462 layoutfont = layout->font;
464 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
465 tmpfont.reduce(layoutfont);
466 par->setFont(pos, tmpfont);
471 Paragraph * LyXText::setLayout(BufferView * bview,
472 LyXCursor & cur, LyXCursor & sstart_cur,
473 LyXCursor & send_cur,
474 string const & layout)
476 Paragraph * endpar = send_cur.par()->next();
477 Paragraph * undoendpar = endpar;
479 if (endpar && endpar->getDepth()) {
480 while (endpar && endpar->getDepth()) {
481 endpar = endpar->next();
485 endpar = endpar->next(); // because of parindents etc.
488 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
490 // ok we have a selection. This is always between sstart_cur
491 // and sel_end cursor
493 Paragraph * par = sstart_cur.par();
494 Paragraph * epar = send_cur.par()->next();
496 LyXLayout_ptr const & lyxlayout =
497 bview->buffer()->params.getLyXTextClass()[layout];
500 par->applyLayout(lyxlayout);
501 makeFontEntriesLayoutSpecific(bview->buffer(), par);
502 Paragraph * fppar = par;
503 fppar->params().spaceTop(lyxlayout->fill_top ?
504 VSpace(VSpace::VFILL)
505 : VSpace(VSpace::NONE));
506 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
507 VSpace(VSpace::VFILL)
508 : VSpace(VSpace::NONE));
509 if (lyxlayout->margintype == MARGIN_MANUAL)
510 par->setLabelWidthString(lyxlayout->labelstring());
511 if (lyxlayout->labeltype != LABEL_BIBLIO
513 delete fppar->bibkey;
518 } while (par != epar);
524 // set layout over selection and make a total rebreak of those paragraphs
525 void LyXText::setLayout(BufferView * bview, string const & layout)
527 LyXCursor tmpcursor = cursor; /* store the current cursor */
529 // if there is no selection just set the layout
530 // of the current paragraph */
531 if (!selection.set()) {
532 selection.start = cursor; // dummy selection
533 selection.end = cursor;
535 Paragraph * endpar = setLayout(bview, cursor, selection.start,
536 selection.end, layout);
537 redoParagraphs(bview, selection.start, endpar);
539 // we have to reset the selection, because the
540 // geometry could have changed
541 setCursor(bview, selection.start.par(),
542 selection.start.pos(), false);
543 selection.cursor = cursor;
544 setCursor(bview, selection.end.par(), selection.end.pos(), false);
545 updateCounters(bview, cursor.row());
548 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
552 // increment depth over selection and
553 // make a total rebreak of those paragraphs
554 void LyXText::incDepth(BufferView * bview)
556 // If there is no selection, just use the current paragraph
557 if (!selection.set()) {
558 selection.start = cursor; // dummy selection
559 selection.end = cursor;
562 // We end at the next paragraph with depth 0
563 Paragraph * endpar = selection.end.par()->next();
565 Paragraph * undoendpar = endpar;
567 if (endpar && endpar->getDepth()) {
568 while (endpar && endpar->getDepth()) {
569 endpar = endpar->next();
573 endpar = endpar->next(); // because of parindents etc.
576 setUndo(bview, Undo::EDIT,
577 selection.start.par(), undoendpar);
579 LyXCursor tmpcursor = cursor; // store the current cursor
581 // ok we have a selection. This is always between sel_start_cursor
582 // and sel_end cursor
583 cursor = selection.start;
585 bool anything_changed = false;
588 // NOTE: you can't change the depth of a bibliography entry
589 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
590 Paragraph * prev = cursor.par()->previous();
593 if (cursor.par()->getDepth()
594 < prev->getMaxDepthAfter()) {
595 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
596 anything_changed = true;
600 if (cursor.par() == selection.end.par())
602 cursor.par(cursor.par()->next());
605 // if nothing changed set all depth to 0
606 if (!anything_changed) {
607 cursor = selection.start;
608 while (cursor.par() != selection.end.par()) {
609 cursor.par()->params().depth(0);
610 cursor.par(cursor.par()->next());
612 cursor.par()->params().depth(0);
615 redoParagraphs(bview, selection.start, endpar);
617 // we have to reset the selection, because the
618 // geometry could have changed
619 setCursor(bview, selection.start.par(), selection.start.pos());
620 selection.cursor = cursor;
621 setCursor(bview, selection.end.par(), selection.end.pos());
622 updateCounters(bview, cursor.row());
625 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
629 // decrement depth over selection and
630 // make a total rebreak of those paragraphs
631 void LyXText::decDepth(BufferView * bview)
633 // if there is no selection just set the layout
634 // of the current paragraph
635 if (!selection.set()) {
636 selection.start = cursor; // dummy selection
637 selection.end = cursor;
639 Paragraph * endpar = selection.end.par()->next();
640 Paragraph * undoendpar = endpar;
642 if (endpar && endpar->getDepth()) {
643 while (endpar && endpar->getDepth()) {
644 endpar = endpar->next();
648 endpar = endpar->next(); // because of parindents etc.
651 setUndo(bview, Undo::EDIT,
652 selection.start.par(), undoendpar);
654 LyXCursor tmpcursor = cursor; // store the current cursor
656 // ok we have a selection. This is always between sel_start_cursor
657 // and sel_end cursor
658 cursor = selection.start;
661 if (cursor.par()->params().depth()) {
662 cursor.par()->params()
663 .depth(cursor.par()->params().depth() - 1);
665 if (cursor.par() == selection.end.par()) {
668 cursor.par(cursor.par()->next());
671 redoParagraphs(bview, selection.start, endpar);
673 // we have to reset the selection, because the
674 // geometry could have changed
675 setCursor(bview, selection.start.par(),
676 selection.start.pos());
677 selection.cursor = cursor;
678 setCursor(bview, selection.end.par(), selection.end.pos());
679 updateCounters(bview, cursor.row());
682 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
686 // set font over selection and make a total rebreak of those paragraphs
687 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
689 // if there is no selection just set the current_font
690 if (!selection.set()) {
691 // Determine basis font
693 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
695 layoutfont = getLabelFont(bview->buffer(),
698 layoutfont = getLayoutFont(bview->buffer(),
701 // Update current font
702 real_current_font.update(font,
703 bview->buffer()->params.language,
706 // Reduce to implicit settings
707 current_font = real_current_font;
708 current_font.reduce(layoutfont);
709 // And resolve it completely
710 #ifndef INHERIT_LANGUAGE
711 real_current_font.realize(layoutfont);
713 real_current_font.realize(layoutfont,
714 bview->buffer()->params.language);
719 LyXCursor tmpcursor = cursor; // store the current cursor
721 // ok we have a selection. This is always between sel_start_cursor
722 // and sel_end cursor
724 setUndo(bview, Undo::EDIT,
725 selection.start.par(), selection.end.par()->next());
727 cursor = selection.start;
728 while (cursor.par() != selection.end.par() ||
729 cursor.pos() < selection.end.pos())
731 if (cursor.pos() < cursor.par()->size()) {
732 // an open footnote should behave like a closed one
733 setCharFont(bview, cursor.par(), cursor.pos(),
735 cursor.pos(cursor.pos() + 1);
738 cursor.par(cursor.par()->next());
743 redoParagraphs(bview, selection.start, selection.end.par()->next());
745 // we have to reset the selection, because the
746 // geometry could have changed, but we keep
747 // it for user convenience
748 setCursor(bview, selection.start.par(), selection.start.pos());
749 selection.cursor = cursor;
750 setCursor(bview, selection.end.par(), selection.end.pos());
752 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
753 tmpcursor.boundary());
757 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
759 Row * tmprow = cur.row();
760 int y = cur.y() - tmprow->baseline();
762 setHeightOfRow(bview, tmprow);
764 while (tmprow->previous()
765 && tmprow->previous()->par() == tmprow->par()) {
766 tmprow = tmprow->previous();
767 y -= tmprow->height();
768 setHeightOfRow(bview, tmprow);
771 // we can set the refreshing parameters now
772 status(bview, LyXText::NEED_MORE_REFRESH);
774 refresh_row = tmprow;
775 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
779 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
781 Row * tmprow = cur.row();
783 int y = cur.y() - tmprow->baseline();
784 setHeightOfRow(bview, tmprow);
786 while (tmprow->previous()
787 && tmprow->previous()->par() == tmprow->par()) {
788 tmprow = tmprow->previous();
789 y -= tmprow->height();
792 // we can set the refreshing parameters now
793 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
795 refresh_row = tmprow;
797 status(bview, LyXText::NEED_MORE_REFRESH);
798 setCursor(bview, cur.par(), cur.pos());
802 // deletes and inserts again all paragaphs between the cursor
803 // and the specified par
804 // This function is needed after SetLayout and SetFont etc.
805 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
806 Paragraph const * endpar) const
809 Paragraph * tmppar = 0;
810 Paragraph * first_phys_par = 0;
812 Row * tmprow = cur.row();
814 int y = cur.y() - tmprow->baseline();
816 if (!tmprow->previous()) {
817 // a trick/hack for UNDO
818 // This is needed because in an UNDO/REDO we could have changed
819 // the ownerParagrah() so the paragraph inside the row is NOT
820 // my really first par anymore. Got it Lars ;) (Jug 20011206)
821 first_phys_par = ownerParagraph();
823 first_phys_par = tmprow->par();
824 while (tmprow->previous()
825 && tmprow->previous()->par() == first_phys_par)
827 tmprow = tmprow->previous();
828 y -= tmprow->height();
832 // we can set the refreshing parameters now
833 status(bview, LyXText::NEED_MORE_REFRESH);
835 refresh_row = tmprow->previous(); /* the real refresh row will
836 be deleted, so I store
840 tmppar = tmprow->next()->par();
843 while (tmprow->next() && tmppar != endpar) {
844 removeRow(tmprow->next());
845 if (tmprow->next()) {
846 tmppar = tmprow->next()->par();
852 // remove the first one
853 tmprow2 = tmprow; /* this is because tmprow->previous()
855 tmprow = tmprow->previous();
858 tmppar = first_phys_par;
862 insertParagraph(bview, tmppar, tmprow);
866 while (tmprow->next()
867 && tmprow->next()->par() == tmppar) {
868 tmprow = tmprow->next();
870 tmppar = tmppar->next();
872 } while (tmppar && tmppar != endpar);
874 // this is because of layout changes
876 refresh_y -= refresh_row->height();
877 setHeightOfRow(bview, refresh_row);
879 refresh_row = firstrow;
881 setHeightOfRow(bview, refresh_row);
884 if (tmprow && tmprow->next())
885 setHeightOfRow(bview, tmprow->next());
889 void LyXText::fullRebreak(BufferView * bview)
895 if (need_break_row) {
896 breakAgain(bview, need_break_row);
903 // important for the screen
906 // the cursor set functions have a special mechanism. When they
907 // realize, that you left an empty paragraph, they will delete it.
908 // They also delete the corresponding row
910 // need the selection cursor:
911 void LyXText::setSelection(BufferView * bview)
913 bool const lsel = selection.set();
915 if (!selection.set()) {
916 last_sel_cursor = selection.cursor;
917 selection.start = selection.cursor;
918 selection.end = selection.cursor;
923 // first the toggling area
924 if (cursor.y() < last_sel_cursor.y()
925 || (cursor.y() == last_sel_cursor.y()
926 && cursor.x() < last_sel_cursor.x())) {
927 toggle_end_cursor = last_sel_cursor;
928 toggle_cursor = cursor;
930 toggle_end_cursor = cursor;
931 toggle_cursor = last_sel_cursor;
934 last_sel_cursor = cursor;
936 // and now the whole selection
938 if (selection.cursor.par() == cursor.par())
939 if (selection.cursor.pos() < cursor.pos()) {
940 selection.end = cursor;
941 selection.start = selection.cursor;
943 selection.end = selection.cursor;
944 selection.start = cursor;
946 else if (selection.cursor.y() < cursor.y() ||
947 (selection.cursor.y() == cursor.y()
948 && selection.cursor.x() < cursor.x())) {
949 selection.end = cursor;
950 selection.start = selection.cursor;
953 selection.end = selection.cursor;
954 selection.start = cursor;
957 // a selection with no contents is not a selection
958 if (selection.start.par() == selection.end.par() &&
959 selection.start.pos() == selection.end.pos())
960 selection.set(false);
962 if (inset_owner && (selection.set() || lsel))
963 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
967 string const LyXText::selectionAsString(Buffer const * buffer,
970 if (!selection.set()) return string();
973 // Special handling if the whole selection is within one paragraph
974 if (selection.start.par() == selection.end.par()) {
975 result += selection.start.par()->asString(buffer,
976 selection.start.pos(),
982 // The selection spans more than one paragraph
984 // First paragraph in selection
985 result += selection.start.par()->asString(buffer,
986 selection.start.pos(),
987 selection.start.par()->size(),
991 // The paragraphs in between (if any)
992 LyXCursor tmpcur(selection.start);
993 tmpcur.par(tmpcur.par()->next());
994 while (tmpcur.par() != selection.end.par()) {
995 result += tmpcur.par()->asString(buffer, 0,
996 tmpcur.par()->size(),
998 tmpcur.par(tmpcur.par()->next());
1001 // Last paragraph in selection
1002 result += selection.end.par()->asString(buffer, 0,
1003 selection.end.pos(), label);
1009 void LyXText::clearSelection() const
1011 selection.set(false);
1012 selection.mark(false);
1013 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1014 // reset this in the bv_owner!
1015 if (bv_owner && bv_owner->text)
1016 bv_owner->text->xsel_cache.set(false);
1020 void LyXText::cursorHome(BufferView * bview) const
1022 setCursor(bview, cursor.par(), cursor.row()->pos());
1026 void LyXText::cursorEnd(BufferView * bview) const
1028 if (!cursor.row()->next()
1029 || cursor.row()->next()->par() != cursor.row()->par()) {
1030 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1032 if (cursor.par()->size() &&
1033 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1034 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1035 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1037 setCursor(bview,cursor.par(),
1038 rowLast(cursor.row()) + 1);
1044 void LyXText::cursorTop(BufferView * bview) const
1046 while (cursor.par()->previous())
1047 cursor.par(cursor.par()->previous());
1048 setCursor(bview, cursor.par(), 0);
1052 void LyXText::cursorBottom(BufferView * bview) const
1054 while (cursor.par()->next())
1055 cursor.par(cursor.par()->next());
1056 setCursor(bview, cursor.par(), cursor.par()->size());
1060 void LyXText::toggleFree(BufferView * bview,
1061 LyXFont const & font, bool toggleall)
1063 // If the mask is completely neutral, tell user
1064 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1065 // Could only happen with user style
1066 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1070 // Try implicit word selection
1071 // If there is a change in the language the implicit word selection
1073 LyXCursor resetCursor = cursor;
1074 bool implicitSelection = (font.language() == ignore_language
1075 && font.number() == LyXFont::IGNORE)
1076 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1079 setFont(bview, font, toggleall);
1081 // Implicit selections are cleared afterwards
1082 //and cursor is set to the original position.
1083 if (implicitSelection) {
1085 cursor = resetCursor;
1086 setCursor(bview, cursor.par(), cursor.pos());
1087 selection.cursor = cursor;
1090 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1094 string LyXText::getStringToIndex(BufferView * bview)
1098 // Try implicit word selection
1099 // If there is a change in the language the implicit word selection
1101 LyXCursor resetCursor = cursor;
1102 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1104 if (!selection.set()) {
1105 bview->owner()->message(_("Nothing to index!"));
1108 if (selection.start.par() != selection.end.par()) {
1109 bview->owner()->message(_("Cannot index more than one paragraph!"));
1113 idxstring = selectionAsString(bview->buffer(), false);
1115 // Implicit selections are cleared afterwards
1116 //and cursor is set to the original position.
1117 if (implicitSelection) {
1119 cursor = resetCursor;
1120 setCursor(bview, cursor.par(), cursor.pos());
1121 selection.cursor = cursor;
1127 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1128 Paragraph const * par) const
1130 if (par->layout()->labeltype != LABEL_MANUAL)
1133 return par->beginningOfMainBody();
1137 // the DTP switches for paragraphs. LyX will store them in the first
1138 // physicla paragraph. When a paragraph is broken, the top settings rest,
1139 // the bottom settings are given to the new one. So I can make shure,
1140 // they do not duplicate themself and you cannnot make dirty things with
1143 void LyXText::setParagraph(BufferView * bview,
1144 bool line_top, bool line_bottom,
1145 bool pagebreak_top, bool pagebreak_bottom,
1146 VSpace const & space_top,
1147 VSpace const & space_bottom,
1148 Spacing const & spacing,
1150 string labelwidthstring,
1153 LyXCursor tmpcursor = cursor;
1154 if (!selection.set()) {
1155 selection.start = cursor;
1156 selection.end = cursor;
1159 // make sure that the depth behind the selection are restored, too
1160 Paragraph * endpar = selection.end.par()->next();
1161 Paragraph * undoendpar = endpar;
1163 if (endpar && endpar->getDepth()) {
1164 while (endpar && endpar->getDepth()) {
1165 endpar = endpar->next();
1166 undoendpar = endpar;
1170 // because of parindents etc.
1171 endpar = endpar->next();
1174 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1177 Paragraph * tmppar = selection.end.par();
1179 while (tmppar != selection.start.par()->previous()) {
1180 setCursor(bview, tmppar, 0);
1181 status(bview, LyXText::NEED_MORE_REFRESH);
1182 refresh_row = cursor.row();
1183 refresh_y = cursor.y() - cursor.row()->baseline();
1184 cursor.par()->params().lineTop(line_top);
1185 cursor.par()->params().lineBottom(line_bottom);
1186 cursor.par()->params().pagebreakTop(pagebreak_top);
1187 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1188 cursor.par()->params().spaceTop(space_top);
1189 cursor.par()->params().spaceBottom(space_bottom);
1190 cursor.par()->params().spacing(spacing);
1191 // does the layout allow the new alignment?
1192 LyXLayout_ptr const & layout = cursor.par()->layout();
1194 if (align == LYX_ALIGN_LAYOUT)
1195 align = layout->align;
1196 if (align & layout->alignpossible) {
1197 if (align == layout->align)
1198 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1200 cursor.par()->params().align(align);
1202 cursor.par()->setLabelWidthString(labelwidthstring);
1203 cursor.par()->params().noindent(noindent);
1204 tmppar = cursor.par()->previous();
1207 redoParagraphs(bview, selection.start, endpar);
1210 setCursor(bview, selection.start.par(), selection.start.pos());
1211 selection.cursor = cursor;
1212 setCursor(bview, selection.end.par(), selection.end.pos());
1213 setSelection(bview);
1214 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1216 bview->updateInset(inset_owner, true);
1220 char loweralphaCounter(int n)
1222 if (n < 1 || n > 26)
1232 char alphaCounter(int n)
1234 if (n < 1 || n > 26)
1242 char hebrewCounter(int n)
1244 static const char hebrew[22] = {
1245 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1246 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1247 '÷', 'ø', 'ù', 'ú'
1249 if (n < 1 || n > 22)
1257 string const romanCounter(int n)
1259 static char const * roman[20] = {
1260 "i", "ii", "iii", "iv", "v",
1261 "vi", "vii", "viii", "ix", "x",
1262 "xi", "xii", "xiii", "xiv", "xv",
1263 "xvi", "xvii", "xviii", "xix", "xx"
1265 if (n < 1 || n > 20)
1274 // set the counter of a paragraph. This includes the labels
1275 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1277 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1278 LyXLayout_ptr const & layout = par->layout();
1280 // copy the prev-counters to this one,
1281 // unless this is the first paragraph
1282 if (par->previous()) {
1283 for (int i = 0; i < 10; ++i) {
1284 par->setCounter(i, par->previous()->getFirstCounter(i));
1286 par->params().appendix(par->previous()->params().appendix());
1287 if (!par->params().appendix() && par->params().startOfAppendix()) {
1288 par->params().appendix(true);
1289 for (int i = 0; i < 10; ++i) {
1290 par->setCounter(i, 0);
1293 par->enumdepth = par->previous()->enumdepth;
1294 par->itemdepth = par->previous()->itemdepth;
1296 for (int i = 0; i < 10; ++i) {
1297 par->setCounter(i, 0);
1299 par->params().appendix(par->params().startOfAppendix());
1304 /* Maybe we have to increment the enumeration depth.
1305 * BUT, enumeration in a footnote is considered in isolation from its
1306 * surrounding paragraph so don't increment if this is the
1307 * first line of the footnote
1308 * AND, bibliographies can't have their depth changed ie. they
1309 * are always of depth 0
1312 && par->previous()->getDepth() < par->getDepth()
1313 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1314 && par->enumdepth < 3
1315 && layout->labeltype != LABEL_BIBLIO) {
1319 // Maybe we have to decrement the enumeration depth, see note above
1321 && par->previous()->getDepth() > par->getDepth()
1322 && layout->labeltype != LABEL_BIBLIO) {
1323 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1324 par->setCounter(6 + par->enumdepth,
1325 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1326 // reset the counters.A depth change is like a breaking layout
1327 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1328 par->setCounter(i, 0);
1331 if (!par->params().labelString().empty()) {
1332 par->params().labelString(string());
1335 if (layout->margintype == MARGIN_MANUAL) {
1336 if (par->params().labelWidthString().empty()) {
1337 par->setLabelWidthString(layout->labelstring());
1340 par->setLabelWidthString(string());
1343 // is it a layout that has an automatic label?
1344 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1346 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1347 if (i >= 0 && i<= buf->params.secnumdepth) {
1348 par->incCounter(i); // increment the counter
1350 // Is there a label? Useful for Chapter layout
1351 if (!par->params().appendix()) {
1352 if (!layout->labelstring().empty())
1353 par->params().labelString(layout->labelstring());
1355 par->params().labelString(string());
1357 if (!layout->labelstring_appendix().empty())
1358 par->params().labelString(layout->labelstring_appendix());
1360 par->params().labelString(string());
1365 if (!par->params().appendix()) {
1366 switch (2 * LABEL_COUNTER_CHAPTER -
1367 textclass.maxcounter() + i) {
1368 case LABEL_COUNTER_CHAPTER:
1369 s << par->getCounter(i);
1371 case LABEL_COUNTER_SECTION:
1372 s << par->getCounter(i - 1) << '.'
1373 << par->getCounter(i);
1375 case LABEL_COUNTER_SUBSECTION:
1376 s << par->getCounter(i - 2) << '.'
1377 << par->getCounter(i - 1) << '.'
1378 << par->getCounter(i);
1380 case LABEL_COUNTER_SUBSUBSECTION:
1381 s << par->getCounter(i - 3) << '.'
1382 << par->getCounter(i - 2) << '.'
1383 << par->getCounter(i - 1) << '.'
1384 << par->getCounter(i);
1387 case LABEL_COUNTER_PARAGRAPH:
1388 s << par->getCounter(i - 4) << '.'
1389 << par->getCounter(i - 3) << '.'
1390 << par->getCounter(i - 2) << '.'
1391 << par->getCounter(i - 1) << '.'
1392 << par->getCounter(i);
1394 case LABEL_COUNTER_SUBPARAGRAPH:
1395 s << par->getCounter(i - 5) << '.'
1396 << par->getCounter(i - 4) << '.'
1397 << par->getCounter(i - 3) << '.'
1398 << par->getCounter(i - 2) << '.'
1399 << par->getCounter(i - 1) << '.'
1400 << par->getCounter(i);
1404 // Can this ever be reached? And in the
1405 // case it is, how can this be correct?
1407 s << par->getCounter(i) << '.';
1410 } else { // appendix
1411 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1412 case LABEL_COUNTER_CHAPTER:
1413 if (par->isRightToLeftPar(buf->params))
1414 s << hebrewCounter(par->getCounter(i));
1416 s << alphaCounter(par->getCounter(i));
1418 case LABEL_COUNTER_SECTION:
1419 if (par->isRightToLeftPar(buf->params))
1420 s << hebrewCounter(par->getCounter(i - 1));
1422 s << alphaCounter(par->getCounter(i - 1));
1425 << par->getCounter(i);
1428 case LABEL_COUNTER_SUBSECTION:
1429 if (par->isRightToLeftPar(buf->params))
1430 s << hebrewCounter(par->getCounter(i - 2));
1432 s << alphaCounter(par->getCounter(i - 2));
1435 << par->getCounter(i-1) << '.'
1436 << par->getCounter(i);
1439 case LABEL_COUNTER_SUBSUBSECTION:
1440 if (par->isRightToLeftPar(buf->params))
1441 s << hebrewCounter(par->getCounter(i-3));
1443 s << alphaCounter(par->getCounter(i-3));
1446 << par->getCounter(i-2) << '.'
1447 << par->getCounter(i-1) << '.'
1448 << par->getCounter(i);
1451 case LABEL_COUNTER_PARAGRAPH:
1452 if (par->isRightToLeftPar(buf->params))
1453 s << hebrewCounter(par->getCounter(i-4));
1455 s << alphaCounter(par->getCounter(i-4));
1458 << par->getCounter(i-3) << '.'
1459 << par->getCounter(i-2) << '.'
1460 << par->getCounter(i-1) << '.'
1461 << par->getCounter(i);
1464 case LABEL_COUNTER_SUBPARAGRAPH:
1465 if (par->isRightToLeftPar(buf->params))
1466 s << hebrewCounter(par->getCounter(i-5));
1468 s << alphaCounter(par->getCounter(i-5));
1471 << par->getCounter(i-4) << '.'
1472 << par->getCounter(i-3) << '.'
1473 << par->getCounter(i-2) << '.'
1474 << par->getCounter(i-1) << '.'
1475 << par->getCounter(i);
1479 // Can this ever be reached? And in the
1480 // case it is, how can this be correct?
1482 s << par->getCounter(i) << '.';
1488 par->params().labelString(par->params().labelString() +s.str().c_str());
1489 // We really want to remove the c_str as soon as
1492 for (i++; i < 10; ++i) {
1493 // reset the following counters
1494 par->setCounter(i, 0);
1496 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1497 for (i++; i < 10; ++i) {
1498 // reset the following counters
1499 par->setCounter(i, 0);
1501 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1502 par->incCounter(i + par->enumdepth);
1503 int number = par->getCounter(i + par->enumdepth);
1507 switch (par->enumdepth) {
1509 if (par->isRightToLeftPar(buf->params))
1511 << hebrewCounter(number)
1515 << loweralphaCounter(number)
1519 if (par->isRightToLeftPar(buf->params))
1520 s << '.' << romanCounter(number);
1522 s << romanCounter(number) << '.';
1525 if (par->isRightToLeftPar(buf->params))
1527 << alphaCounter(number);
1529 s << alphaCounter(number)
1533 if (par->isRightToLeftPar(buf->params))
1540 par->params().labelString(s.str().c_str());
1542 for (i += par->enumdepth + 1; i < 10; ++i) {
1543 // reset the following counters
1544 par->setCounter(i, 0);
1548 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1549 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1551 int number = par->getCounter(i);
1553 InsetCommandParams p("bibitem" );
1554 par->bibkey = new InsetBibKey(p);
1556 par->bibkey->setCounter(number);
1557 par->params().labelString(layout->labelstring());
1559 // In biblio should't be following counters but...
1561 string s = layout->labelstring();
1563 // the caption hack:
1564 if (layout->labeltype == LABEL_SENSITIVE) {
1565 bool isOK (par->inInset() && par->inInset()->owner() &&
1566 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1569 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1571 = floatList.getType(tmp->type());
1572 // We should get the correct number here too.
1573 s = fl.name() + " #:";
1575 /* par->SetLayout(0);
1576 s = layout->labelstring; */
1577 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1578 ? " :úåòîùî øñç" : "Senseless: ";
1581 par->params().labelString(s);
1583 /* reset the enumeration counter. They are always resetted
1584 * when there is any other layout between */
1585 for (int i = 6 + par->enumdepth; i < 10; ++i)
1586 par->setCounter(i, 0);
1591 // Updates all counters BEHIND the row. Changed paragraphs
1592 // with a dynamic left margin will be rebroken.
1593 void LyXText::updateCounters(BufferView * bview, Row * row) const
1601 par = row->par()->next();
1605 while (row->par() != par)
1608 setCounter(bview->buffer(), par);
1610 // now check for the headline layouts. remember that they
1611 // have a dynamic left margin
1612 LyXLayout_ptr const & layout = par->layout();
1614 if (layout->margintype == MARGIN_DYNAMIC
1615 || layout->labeltype == LABEL_SENSITIVE) {
1616 // Rebreak the paragraph
1617 removeParagraph(row);
1618 appendParagraph(bview, row);
1625 void LyXText::insertInset(BufferView * bview, Inset * inset)
1627 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1629 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1631 cursor.par()->insertInset(cursor.pos(), inset);
1632 // Just to rebreak and refresh correctly.
1633 // The character will not be inserted a second time
1634 insertChar(bview, Paragraph::META_INSET);
1635 // If we enter a highly editable inset the cursor should be to before
1636 // the inset. This couldn't happen before as Undo was not handled inside
1637 // inset now after the Undo LyX tries to call inset->Edit(...) again
1638 // and cannot do this as the cursor is behind the inset and GetInset
1639 // does not return the inset!
1640 if (isHighlyEditableInset(inset)) {
1641 cursorLeft(bview, true);
1647 void LyXText::copyEnvironmentType()
1649 copylayouttype = cursor.par()->layout()->name();
1653 void LyXText::pasteEnvironmentType(BufferView * bview)
1655 setLayout(bview, copylayouttype);
1659 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1661 // Stuff what we got on the clipboard. Even if there is no selection.
1663 // There is a problem with having the stuffing here in that the
1664 // larger the selection the slower LyX will get. This can be
1665 // solved by running the line below only when the selection has
1666 // finished. The solution used currently just works, to make it
1667 // faster we need to be more clever and probably also have more
1668 // calls to stuffClipboard. (Lgb)
1669 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1671 // This doesn't make sense, if there is no selection
1672 if (!selection.set())
1675 // OK, we have a selection. This is always between selection.start
1676 // and selection.end
1678 // make sure that the depth behind the selection are restored, too
1679 Paragraph * endpar = selection.end.par()->next();
1680 Paragraph * undoendpar = endpar;
1682 if (endpar && endpar->getDepth()) {
1683 while (endpar && endpar->getDepth()) {
1684 endpar = endpar->next();
1685 undoendpar = endpar;
1687 } else if (endpar) {
1688 endpar = endpar->next(); // because of parindents etc.
1691 setUndo(bview, Undo::DELETE,
1692 selection.start.par(), undoendpar);
1694 // there are two cases: cut only within one paragraph or
1695 // more than one paragraph
1696 if (selection.start.par() == selection.end.par()) {
1697 // only within one paragraph
1698 endpar = selection.end.par();
1699 int pos = selection.end.pos();
1700 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1701 selection.start.pos(), pos,
1702 bview->buffer()->params.textclass,
1704 selection.end.pos(pos);
1706 endpar = selection.end.par();
1707 int pos = selection.end.pos();
1708 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1709 selection.start.pos(), pos,
1710 bview->buffer()->params.textclass,
1713 selection.end.par(endpar);
1714 selection.end.pos(pos);
1715 cursor.pos(selection.end.pos());
1717 endpar = endpar->next();
1719 // sometimes necessary
1721 selection.start.par()->stripLeadingSpaces();
1723 redoParagraphs(bview, selection.start, endpar);
1725 // cutSelection can invalidate the cursor so we need to set
1727 cursor = selection.start;
1729 // need a valid cursor. (Lgb)
1732 setCursor(bview, cursor.par(), cursor.pos());
1733 selection.cursor = cursor;
1734 updateCounters(bview, cursor.row());
1738 void LyXText::copySelection(BufferView * bview)
1740 // stuff the selection onto the X clipboard, from an explicit copy request
1741 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1743 // this doesnt make sense, if there is no selection
1744 if (!selection.set())
1747 // ok we have a selection. This is always between selection.start
1748 // and sel_end cursor
1750 // copy behind a space if there is one
1751 while (selection.start.par()->size() > selection.start.pos()
1752 && selection.start.par()->isLineSeparator(selection.start.pos())
1753 && (selection.start.par() != selection.end.par()
1754 || selection.start.pos() < selection.end.pos()))
1755 selection.start.pos(selection.start.pos() + 1);
1757 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1758 selection.start.pos(), selection.end.pos(),
1759 bview->buffer()->params.textclass);
1763 void LyXText::pasteSelection(BufferView * bview)
1765 // this does not make sense, if there is nothing to paste
1766 if (!CutAndPaste::checkPastePossible(cursor.par()))
1769 setUndo(bview, Undo::INSERT,
1770 cursor.par(), cursor.par()->next());
1773 Paragraph * actpar = cursor.par();
1774 int pos = cursor.pos();
1776 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1777 bview->buffer()->params.textclass);
1779 redoParagraphs(bview, cursor, endpar);
1781 setCursor(bview, cursor.par(), cursor.pos());
1784 selection.cursor = cursor;
1785 setCursor(bview, actpar, pos);
1786 setSelection(bview);
1787 updateCounters(bview, cursor.row());
1791 // sets the selection over the number of characters of string, no check!!
1792 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1797 selection.cursor = cursor;
1798 for (string::size_type i = 0; i < str.length(); ++i)
1800 setSelection(bview);
1804 // simple replacing. The font of the first selected character is used
1805 void LyXText::replaceSelectionWithString(BufferView * bview,
1808 setCursorParUndo(bview);
1811 if (!selection.set()) { // create a dummy selection
1812 selection.end = cursor;
1813 selection.start = cursor;
1816 // Get font setting before we cut
1817 pos_type pos = selection.end.pos();
1818 LyXFont const font = selection.start.par()
1819 ->getFontSettings(bview->buffer()->params,
1820 selection.start.pos());
1822 // Insert the new string
1823 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1824 selection.end.par()->insertChar(pos, (*cit), font);
1828 // Cut the selection
1829 cutSelection(bview, true, false);
1835 // needed to insert the selection
1836 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1838 Paragraph * par = cursor.par();
1839 pos_type pos = cursor.pos();
1840 Paragraph * endpar = cursor.par()->next();
1842 setCursorParUndo(bview);
1844 // only to be sure, should not be neccessary
1847 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1849 redoParagraphs(bview, cursor, endpar);
1850 setCursor(bview, cursor.par(), cursor.pos());
1851 selection.cursor = cursor;
1852 setCursor(bview, par, pos);
1853 setSelection(bview);
1857 // turns double-CR to single CR, others where converted into one
1858 // blank. Then InsertStringAsLines is called
1859 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1861 string linestr(str);
1862 bool newline_inserted = false;
1863 for (string::size_type i = 0; i < linestr.length(); ++i) {
1864 if (linestr[i] == '\n') {
1865 if (newline_inserted) {
1866 // we know that \r will be ignored by
1867 // InsertStringA. Of course, it is a dirty
1868 // trick, but it works...
1869 linestr[i - 1] = '\r';
1873 newline_inserted = true;
1875 } else if (IsPrintable(linestr[i])) {
1876 newline_inserted = false;
1879 insertStringAsLines(bview, linestr);
1883 bool LyXText::gotoNextInset(BufferView * bview,
1884 vector<Inset::Code> const & codes,
1885 string const & contents) const
1887 LyXCursor res = cursor;
1890 if (res.pos() < res.par()->size() - 1) {
1891 res.pos(res.pos() + 1);
1893 res.par(res.par()->next());
1897 } while (res.par() &&
1898 !(res.par()->isInset(res.pos())
1899 && (inset = res.par()->getInset(res.pos())) != 0
1900 && find(codes.begin(), codes.end(), inset->lyxCode())
1902 && (contents.empty() ||
1903 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1907 setCursor(bview, res.par(), res.pos(), false);
1914 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1917 LyXCursor tmpcursor;
1921 Row * row = getRow(par, pos, y);
1923 // is there a break one row above
1924 if (row->previous() && row->previous()->par() == row->par()) {
1925 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1926 if (z >= row->pos()) {
1927 // set the dimensions of the row above
1928 y -= row->previous()->height();
1930 refresh_row = row->previous();
1931 status(bview, LyXText::NEED_MORE_REFRESH);
1933 breakAgain(bview, row->previous());
1935 // set the cursor again. Otherwise
1936 // dangling pointers are possible
1937 setCursor(bview, cursor.par(), cursor.pos(),
1938 false, cursor.boundary());
1939 selection.cursor = cursor;
1944 int const tmpheight = row->height();
1945 pos_type const tmplast = rowLast(row);
1949 breakAgain(bview, row);
1950 if (row->height() == tmpheight && rowLast(row) == tmplast)
1951 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1953 status(bview, LyXText::NEED_MORE_REFRESH);
1955 // check the special right address boxes
1956 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1963 redoDrawingOfParagraph(bview, tmpcursor);
1966 // set the cursor again. Otherwise dangling pointers are possible
1967 // also set the selection
1969 if (selection.set()) {
1971 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1972 false, selection.cursor.boundary());
1973 selection.cursor = cursor;
1974 setCursorIntern(bview, selection.start.par(),
1975 selection.start.pos(),
1976 false, selection.start.boundary());
1977 selection.start = cursor;
1978 setCursorIntern(bview, selection.end.par(),
1979 selection.end.pos(),
1980 false, selection.end.boundary());
1981 selection.end = cursor;
1982 setCursorIntern(bview, last_sel_cursor.par(),
1983 last_sel_cursor.pos(),
1984 false, last_sel_cursor.boundary());
1985 last_sel_cursor = cursor;
1988 setCursorIntern(bview, cursor.par(), cursor.pos(),
1989 false, cursor.boundary());
1993 // returns false if inset wasn't found
1994 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1996 // first check the current paragraph
1997 int pos = cursor.par()->getPositionOfInset(inset);
1999 checkParagraph(bview, cursor.par(), pos);
2003 // check every paragraph
2005 Paragraph * par = ownerParagraph();
2007 pos = par->getPositionOfInset(inset);
2009 checkParagraph(bview, par, pos);
2019 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2021 bool setfont, bool boundary) const
2023 LyXCursor old_cursor = cursor;
2024 setCursorIntern(bview, par, pos, setfont, boundary);
2025 return deleteEmptyParagraphMechanism(bview, old_cursor);
2029 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2030 pos_type pos, bool boundary) const
2037 cur.boundary(boundary);
2039 // get the cursor y position in text
2041 Row * row = getRow(par, pos, y);
2042 Row * old_row = row;
2044 // if we are before the first char of this row and are still in the
2045 // same paragraph and there is a previous row then put the cursor on
2046 // the end of the previous row
2047 cur.iy(y + row->baseline());
2049 if (row->previous() && pos &&
2050 row->previous()->par() == row->par() &&
2051 par->getChar(pos) == Paragraph::META_INSET &&
2052 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2054 row = row->previous();
2059 // y is now the beginning of the cursor row
2060 y += row->baseline();
2061 // y is now the cursor baseline
2064 pos_type last = rowLastPrintable(old_row);
2066 if (pos > last + 1) {
2067 // This shouldn't happen.
2070 } else if (pos < row->pos()) {
2075 // now get the cursors x position
2076 float x = getCursorX(bview, row, pos, last, boundary);
2079 if (old_row != row) {
2080 x = getCursorX(bview, old_row, pos, last, boundary);
2087 float LyXText::getCursorX(BufferView * bview, Row * row,
2088 pos_type pos, pos_type last, bool boundary) const
2090 pos_type cursor_vpos = 0;
2092 float fill_separator;
2094 float fill_label_hfill;
2095 // This call HAS to be here because of the BidiTables!!!
2096 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2099 if (last < row->pos())
2100 cursor_vpos = row->pos();
2101 else if (pos > last && !boundary)
2102 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2103 ? row->pos() : last + 1;
2104 else if (pos > row->pos() &&
2105 (pos > last || boundary))
2106 /// Place cursor after char at (logical) position pos - 1
2107 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2108 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2110 /// Place cursor before char at (logical) position pos
2111 cursor_vpos = (bidi_level(pos) % 2 == 0)
2112 ? log2vis(pos) : log2vis(pos) + 1;
2114 pos_type main_body =
2115 beginningOfMainBody(bview->buffer(), row->par());
2116 if ((main_body > 0) &&
2117 ((main_body-1 > last) ||
2118 !row->par()->isLineSeparator(main_body-1)))
2121 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2122 pos_type pos = vis2log(vpos);
2123 if (main_body > 0 && pos == main_body - 1) {
2124 x += fill_label_hfill +
2125 font_metrics::width(
2126 row->par()->layout()->labelsep,
2127 getLabelFont(bview->buffer(),
2129 if (row->par()->isLineSeparator(main_body - 1))
2130 x -= singleWidth(bview,
2131 row->par(), main_body - 1);
2133 if (hfillExpansion(bview->buffer(), row, pos)) {
2134 x += singleWidth(bview, row->par(), pos);
2135 if (pos >= main_body)
2138 x += fill_label_hfill;
2139 } else if (row->par()->isSeparator(pos)) {
2140 x += singleWidth(bview, row->par(), pos);
2141 if (pos >= main_body)
2142 x += fill_separator;
2144 x += singleWidth(bview, row->par(), pos);
2150 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2151 pos_type pos, bool setfont, bool boundary) const
2153 InsetText * it = static_cast<InsetText *>(par->inInset());
2155 if (it != inset_owner) {
2156 lyxerr[Debug::INSETS] << "InsetText is " << it
2158 << "inset_owner is "
2159 << inset_owner << endl;
2160 #ifdef WITH_WARNINGS
2161 #warning I believe this code is wrong. (Lgb)
2162 #warning Jürgen, have a look at this. (Lgb)
2163 #warning Hmmm, I guess you are right but we
2164 #warning should verify when this is needed
2166 // Jürgen, would you like to have a look?
2167 // I guess we need to move the outer cursor
2168 // and open and lock the inset (bla bla bla)
2169 // stuff I don't know... so can you have a look?
2171 // I moved the lyxerr stuff in here so we can see if
2172 // this is actually really needed and where!
2174 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2179 setCursor(bview, cursor, par, pos, boundary);
2181 setCurrentFont(bview);
2185 void LyXText::setCurrentFont(BufferView * bview) const
2187 pos_type pos = cursor.pos();
2188 if (cursor.boundary() && pos > 0)
2192 if (pos == cursor.par()->size())
2194 else // potentional bug... BUG (Lgb)
2195 if (cursor.par()->isSeparator(pos)) {
2196 if (pos > cursor.row()->pos() &&
2197 bidi_level(pos) % 2 ==
2198 bidi_level(pos - 1) % 2)
2200 else if (pos + 1 < cursor.par()->size())
2206 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2207 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2209 if (cursor.pos() == cursor.par()->size() &&
2210 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2211 !cursor.boundary()) {
2212 Language const * lang =
2213 cursor.par()->getParLanguage(bview->buffer()->params);
2214 current_font.setLanguage(lang);
2215 current_font.setNumber(LyXFont::OFF);
2216 real_current_font.setLanguage(lang);
2217 real_current_font.setNumber(LyXFont::OFF);
2222 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2224 LyXCursor old_cursor = cursor;
2226 setCursorFromCoordinates(bview, cursor, x, y);
2227 setCurrentFont(bview);
2228 deleteEmptyParagraphMechanism(bview, old_cursor);
2232 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2235 // Get the row first.
2237 Row * row = getRowNearY(y);
2239 pos_type const column = getColumnNearX(bview, row, x, bound);
2240 cur.par(row->par());
2241 cur.pos(row->pos() + column);
2243 cur.y(y + row->baseline());
2246 if (row->next() && row->next()->pos() == cur.pos() &&
2247 cur.par() == row->next()->par() &&
2248 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2249 (ins=cur.par()->getInset(cur.pos())) &&
2250 (ins->needFullRow() || ins->display()))
2252 // we enter here if we put the cursor on the end of the row before
2253 // a inset which uses a full row and in that case we HAVE to calculate
2254 // the right (i) values.
2255 pos_type last = rowLastPrintable(row);
2256 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2258 cur.iy(y + row->height() + row->next()->baseline());
2259 cur.irow(row->next());
2265 cur.boundary(bound);
2269 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2271 if (cursor.pos() > 0) {
2272 bool boundary = cursor.boundary();
2273 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2274 if (!internal && !boundary &&
2275 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2276 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2277 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2278 Paragraph * par = cursor.par()->previous();
2279 setCursor(bview, par, par->size());
2284 void LyXText::cursorRight(BufferView * bview, bool internal) const
2286 if (!internal && cursor.boundary() &&
2287 !cursor.par()->isNewline(cursor.pos()))
2288 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2289 else if (cursor.pos() < cursor.par()->size()) {
2290 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2292 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2293 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2294 } else if (cursor.par()->next())
2295 setCursor(bview, cursor.par()->next(), 0);
2299 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2302 int x = cursor.x_fix();
2303 int y = cursor.y() - cursor.row()->baseline() - 1;
2304 setCursorFromCoordinates(bview, x, y);
2306 int y1 = cursor.iy() - first_y;
2310 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2311 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2312 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2316 setCursorFromCoordinates(bview, cursor.x_fix(),
2317 cursor.y() - cursor.row()->baseline() - 1);
2322 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2325 int x = cursor.x_fix();
2326 int y = cursor.y() - cursor.row()->baseline() +
2327 cursor.row()->height() + 1;
2328 setCursorFromCoordinates(bview, x, y);
2329 if (!selecting && cursor.row() == cursor.irow()) {
2330 int y1 = cursor.iy() - first_y;
2334 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2335 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2336 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2340 setCursorFromCoordinates(bview, cursor.x_fix(),
2341 cursor.y() - cursor.row()->baseline()
2342 + cursor.row()->height() + 1);
2347 void LyXText::cursorUpParagraph(BufferView * bview) const
2349 if (cursor.pos() > 0) {
2350 setCursor(bview, cursor.par(), 0);
2352 else if (cursor.par()->previous()) {
2353 setCursor(bview, cursor.par()->previous(), 0);
2358 void LyXText::cursorDownParagraph(BufferView * bview) const
2360 if (cursor.par()->next()) {
2361 setCursor(bview, cursor.par()->next(), 0);
2363 setCursor(bview, cursor.par(), cursor.par()->size());
2367 // fix the cursor `cur' after a characters has been deleted at `where'
2368 // position. Called by deleteEmptyParagraphMechanism
2369 void LyXText::fixCursorAfterDelete(BufferView * bview,
2371 LyXCursor const & where) const
2373 // if cursor is not in the paragraph where the delete occured,
2375 if (cur.par() != where.par())
2378 // if cursor position is after the place where the delete occured,
2380 if (cur.pos() > where.pos())
2381 cur.pos(cur.pos()-1);
2383 // check also if we don't want to set the cursor on a spot behind the
2384 // pagragraph because we erased the last character.
2385 if (cur.pos() > cur.par()->size())
2386 cur.pos(cur.par()->size());
2388 // recompute row et al. for this cursor
2389 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2393 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2394 LyXCursor const & old_cursor) const
2396 // Would be wrong to delete anything if we have a selection.
2397 if (selection.set())
2400 // We allow all kinds of "mumbo-jumbo" when freespacing.
2401 if (old_cursor.par()->layout()->free_spacing
2402 || old_cursor.par()->isFreeSpacing()) {
2406 /* Ok I'll put some comments here about what is missing.
2407 I have fixed BackSpace (and thus Delete) to not delete
2408 double-spaces automagically. I have also changed Cut,
2409 Copy and Paste to hopefully do some sensible things.
2410 There are still some small problems that can lead to
2411 double spaces stored in the document file or space at
2412 the beginning of paragraphs. This happens if you have
2413 the cursor betwenn to spaces and then save. Or if you
2414 cut and paste and the selection have a space at the
2415 beginning and then save right after the paste. I am
2416 sure none of these are very hard to fix, but I will
2417 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2418 that I can get some feedback. (Lgb)
2421 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2422 // delete the LineSeparator.
2425 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2426 // delete the LineSeparator.
2429 // If the pos around the old_cursor were spaces, delete one of them.
2430 if (old_cursor.par() != cursor.par()
2431 || old_cursor.pos() != cursor.pos()) {
2432 // Only if the cursor has really moved
2434 if (old_cursor.pos() > 0
2435 && old_cursor.pos() < old_cursor.par()->size()
2436 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2437 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2438 old_cursor.par()->erase(old_cursor.pos() - 1);
2439 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2441 #ifdef WITH_WARNINGS
2442 #warning This will not work anymore when we have multiple views of the same buffer
2443 // In this case, we will have to correct also the cursors held by
2444 // other bufferviews. It will probably be easier to do that in a more
2445 // automated way in LyXCursor code. (JMarc 26/09/2001)
2447 // correct all cursors held by the LyXText
2448 fixCursorAfterDelete(bview, cursor, old_cursor);
2449 fixCursorAfterDelete(bview, selection.cursor,
2451 fixCursorAfterDelete(bview, selection.start,
2453 fixCursorAfterDelete(bview, selection.end, old_cursor);
2454 fixCursorAfterDelete(bview, last_sel_cursor,
2456 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2457 fixCursorAfterDelete(bview, toggle_end_cursor,
2463 // don't delete anything if this is the ONLY paragraph!
2464 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2467 // Do not delete empty paragraphs with keepempty set.
2468 if (old_cursor.par()->layout()->keepempty)
2471 // only do our magic if we changed paragraph
2472 if (old_cursor.par() == cursor.par())
2475 // record if we have deleted a paragraph
2476 // we can't possibly have deleted a paragraph before this point
2477 bool deleted = false;
2479 if ((old_cursor.par()->size() == 0
2480 || (old_cursor.par()->size() == 1
2481 && old_cursor.par()->isLineSeparator(0)))) {
2482 // ok, we will delete anything
2483 LyXCursor tmpcursor;
2485 // make sure that you do not delete any environments
2486 status(bview, LyXText::NEED_MORE_REFRESH);
2489 if (old_cursor.row()->previous()) {
2490 refresh_row = old_cursor.row()->previous();
2491 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2493 cursor = old_cursor; // that undo can restore the right cursor position
2494 Paragraph * endpar = old_cursor.par()->next();
2495 if (endpar && endpar->getDepth()) {
2496 while (endpar && endpar->getDepth()) {
2497 endpar = endpar->next();
2500 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2504 removeRow(old_cursor.row());
2505 if (ownerParagraph() == old_cursor.par()) {
2506 ownerParagraph(ownerParagraph()->next());
2509 delete old_cursor.par();
2511 /* Breakagain the next par. Needed because of
2512 * the parindent that can occur or dissappear.
2513 * The next row can change its height, if
2514 * there is another layout before */
2515 if (refresh_row->next()) {
2516 breakAgain(bview, refresh_row->next());
2517 updateCounters(bview, refresh_row);
2519 setHeightOfRow(bview, refresh_row);
2521 refresh_row = old_cursor.row()->next();
2522 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2525 cursor = old_cursor; // that undo can restore the right cursor position
2526 Paragraph * endpar = old_cursor.par()->next();
2527 if (endpar && endpar->getDepth()) {
2528 while (endpar && endpar->getDepth()) {
2529 endpar = endpar->next();
2532 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2536 removeRow(old_cursor.row());
2538 if (ownerParagraph() == old_cursor.par()) {
2539 ownerParagraph(ownerParagraph()->next());
2542 delete old_cursor.par();
2544 /* Breakagain the next par. Needed because of
2545 the parindent that can occur or dissappear.
2546 The next row can change its height, if
2547 there is another layout before */
2549 breakAgain(bview, refresh_row);
2550 updateCounters(bview, refresh_row->previous());
2555 setCursorIntern(bview, cursor.par(), cursor.pos());
2557 if (selection.cursor.par() == old_cursor.par()
2558 && selection.cursor.pos() == old_cursor.pos()) {
2559 // correct selection
2560 selection.cursor = cursor;
2564 if (old_cursor.par()->stripLeadingSpaces()) {
2565 redoParagraphs(bview, old_cursor,
2566 old_cursor.par()->next());
2568 setCursorIntern(bview, cursor.par(), cursor.pos());
2569 selection.cursor = cursor;
2576 void LyXText::toggleAppendix(BufferView * bview)
2578 Paragraph * par = cursor.par();
2579 bool start = !par->params().startOfAppendix();
2581 // ensure that we have only one start_of_appendix in this document
2582 Paragraph * tmp = ownerParagraph();
2583 for (; tmp; tmp = tmp->next()) {
2584 tmp->params().startOfAppendix(false);
2587 par->params().startOfAppendix(start);
2589 // we can set the refreshing parameters now
2590 status(bview, LyXText::NEED_MORE_REFRESH);
2592 refresh_row = 0; // not needed for full update
2593 updateCounters(bview, 0);
2594 setCursor(bview, cursor.par(), cursor.pos());
2598 Paragraph * LyXText::ownerParagraph() const
2601 return inset_owner->paragraph();
2603 return bv_owner->buffer()->paragraph;
2607 void LyXText::ownerParagraph(Paragraph * p) const
2610 inset_owner->paragraph(p);
2612 bv_owner->buffer()->paragraph = p;
2617 void LyXText::ownerParagraph(int id, Paragraph * p) const
2619 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2620 if (op && op->inInset()) {
2621 static_cast<InsetText *>(op->inInset())->paragraph(p);
2628 LyXText::text_status LyXText::status() const
2634 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2636 // We should only go up with refreshing code so this means that if
2637 // we have a MORE refresh we should never set it to LITTLE if we still
2638 // didn't handle it (and then it will be UNCHANGED. Now as long as
2639 // we stay inside one LyXText this may work but we need to tell the
2640 // outermost LyXText that it should REALLY draw us if there is some
2641 // change in a Inset::LyXText. So you see that when we are inside a
2642 // inset's LyXText we give the LITTLE to the outermost LyXText to
2643 // tell'em that it should redraw the actual row (where the inset
2644 // resides! Capito?!
2646 if ((status_ != NEED_MORE_REFRESH)
2647 || (status_ == NEED_MORE_REFRESH
2648 && st != NEED_VERY_LITTLE_REFRESH))
2651 if (inset_owner && st != UNCHANGED) {
2652 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2653 if (!bview->text->refresh_row) {
2654 bview->text->refresh_row = bview->text->cursor.row();
2655 bview->text->refresh_y = bview->text->cursor.y() -
2656 bview->text->cursor.row()->baseline();