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"
37 #include "insets/inseterror.h"
38 #include "insets/insetbib.h"
39 #include "insets/insetspecialchar.h"
40 #include "insets/insettext.h"
41 #include "insets/insetfloat.h"
43 #include "support/LAssert.h"
44 #include "support/textutils.h"
45 #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 // We specialize the 95% common case:
167 if (!par->getDepth()) {
168 if (layout->labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params, pos);
173 par->inInset()->getDrawFont(f);
174 #ifndef INHERIT_LANGUAGE
175 return f.realize(layout->reslabelfont);
177 return f.realize(layout.reslabelfont, buf->params.language);
180 LyXFont f = par->getFontSettings(buf->params, pos);
182 par->inInset()->getDrawFont(f);
183 #ifndef INHERIT_LANGUAGE
184 return f.realize(layout->resfont);
186 return f.realize(layout.resfont, buf->params.language);
191 // The uncommon case need not be optimized as much
195 if (pos < beginningOfMainBody(buf, par)) {
197 layoutfont = layout->labelfont;
200 layoutfont = layout->font;
203 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
204 #ifndef INHERIT_LANGUAGE
205 tmpfont.realize(layoutfont);
207 tmpfont.realize(layoutfont, buf->params.language);
210 par->inInset()->getDrawFont(tmpfont);
212 return realizeFont(tmpfont, buf, par);
216 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
218 LyXLayout_ptr const & layout = par->layout();
220 if (!par->getDepth()) {
221 return layout->resfont;
224 return realizeFont(layout->font, buf, par);
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
230 LyXLayout_ptr const & layout = par->layout();
232 if (!par->getDepth()) {
233 return layout->reslabelfont;
236 return realizeFont(layout->labelfont, buf, par);
240 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
241 pos_type pos, LyXFont const & fnt,
244 Buffer const * buf = bv->buffer();
245 LyXFont font = getFont(buf, par, pos);
246 font.update(fnt, buf->params.language, toggleall);
247 // Let the insets convert their font
248 if (par->isInset(pos)) {
249 Inset * inset = par->getInset(pos);
250 if (isEditableInset(inset)) {
251 UpdatableInset * uinset =
252 static_cast<UpdatableInset *>(inset);
253 uinset->setFont(bv, fnt, toggleall, true);
257 // Plug thru to version below:
258 setCharFont(buf, par, pos, font);
262 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
263 pos_type pos, LyXFont const & fnt)
267 LyXTextClass const & tclass = buf->params.getLyXTextClass();
268 LyXLayout_ptr const & layout = par->layout();
270 // Get concrete layout font to reduce against
273 if (pos < beginningOfMainBody(buf, par))
274 layoutfont = layout->labelfont;
276 layoutfont = layout->font;
278 // Realize against environment font information
279 if (par->getDepth()) {
280 Paragraph * tp = par;
281 while (!layoutfont.resolved() && tp && tp->getDepth()) {
282 tp = tp->outerHook();
284 #ifndef INHERIT_LANGUAGE
285 layoutfont.realize(tp->layout()->font);
287 layoutfont.realize(tclass[tp->layout()].font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(tclass.defaultfont());
296 layoutfont.realize(tclass.defaultfont(), buf->params.language);
299 // Now, reduce font against full layout font
300 font.reduce(layoutfont);
302 par->setFont(pos, font);
306 // inserts a new row behind the specified row, increments
307 // the touched counters
308 void LyXText::insertRow(Row * row, Paragraph * par,
311 Row * tmprow = new Row;
314 tmprow->next(firstrow);
317 tmprow->previous(row);
318 tmprow->next(row->next());
323 tmprow->next()->previous(tmprow);
325 if (tmprow->previous())
326 tmprow->previous()->next(tmprow);
338 // removes the row and reset the touched counters
339 void LyXText::removeRow(Row * row) const
341 Row * row_prev = row->previous();
343 row->next()->previous(row_prev);
345 firstrow = row->next();
346 // lyx::Assert(firstrow);
348 row_prev->next(row->next());
350 if (row == lastrow) {
351 lyx::Assert(!row->next());
354 if (refresh_row == row) {
355 refresh_row = row_prev ? row_prev : row->next();
356 // what about refresh_y, refresh_height
359 height -= row->height(); // the text becomes smaller
362 --number_of_rows; // one row less
366 // remove all following rows of the paragraph of the specified row.
367 void LyXText::removeParagraph(Row * row) const
369 Paragraph * tmppar = row->par();
373 while (row && row->par() == tmppar) {
374 tmprow = row->next();
381 // insert the specified paragraph behind the specified row
382 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
385 // insert a new row, starting at position 0
386 insertRow(row, par, 0);
389 setCounter(bview->buffer(), par);
391 // and now append the whole paragraph behind the new row
394 appendParagraph(bview, firstrow);
396 row->next()->height(0);
397 appendParagraph(bview, row->next());
402 Inset * LyXText::getInset() const
405 if (cursor.pos() == 0 && cursor.par()->bibkey) {
406 inset = cursor.par()->bibkey;
407 } else if (cursor.pos() < cursor.par()->size()
408 && cursor.par()->isInset(cursor.pos())) {
409 inset = cursor.par()->getInset(cursor.pos());
415 void LyXText::toggleInset(BufferView * bview)
417 Inset * inset = getInset();
418 // is there an editable inset at cursor position?
419 if (!isEditableInset(inset)) {
420 // No, try to see if we are inside a collapsable inset
421 if (inset_owner && inset_owner->owner()
422 && inset_owner->owner()->isOpen()) {
423 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
424 inset_owner->owner()->close(bview);
425 bview->getLyXText()->cursorRight(bview);
429 //bview->owner()->message(inset->editMessage());
431 // do we want to keep this?? (JMarc)
432 if (!isHighlyEditableInset(inset))
433 setCursorParUndo(bview);
435 if (inset->isOpen()) {
441 inset->open(bview, !inset->isOpen());
446 /* used in setlayout */
447 // Asger is not sure we want to do this...
448 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
451 LyXLayout_ptr const & layout = par->layout();
454 for (pos_type pos = 0; pos < par->size(); ++pos) {
455 if (pos < beginningOfMainBody(buf, par))
456 layoutfont = layout->labelfont;
458 layoutfont = layout->font;
460 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
461 tmpfont.reduce(layoutfont);
462 par->setFont(pos, tmpfont);
467 Paragraph * LyXText::setLayout(BufferView * bview,
468 LyXCursor & cur, LyXCursor & sstart_cur,
469 LyXCursor & send_cur,
470 string const & layout)
472 Paragraph * endpar = send_cur.par()->next();
473 Paragraph * undoendpar = endpar;
475 if (endpar && endpar->getDepth()) {
476 while (endpar && endpar->getDepth()) {
477 endpar = endpar->next();
481 endpar = endpar->next(); // because of parindents etc.
484 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
486 // ok we have a selection. This is always between sstart_cur
487 // and sel_end cursor
489 Paragraph * par = sstart_cur.par();
490 Paragraph * epar = send_cur.par()->next();
492 LyXLayout_ptr const & lyxlayout =
493 bview->buffer()->params.getLyXTextClass()[layout];
496 par->applyLayout(lyxlayout);
497 makeFontEntriesLayoutSpecific(bview->buffer(), par);
498 Paragraph * fppar = par;
499 fppar->params().spaceTop(lyxlayout->fill_top ?
500 VSpace(VSpace::VFILL)
501 : VSpace(VSpace::NONE));
502 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
503 VSpace(VSpace::VFILL)
504 : VSpace(VSpace::NONE));
505 if (lyxlayout->margintype == MARGIN_MANUAL)
506 par->setLabelWidthString(lyxlayout->labelstring());
507 if (lyxlayout->labeltype != LABEL_BIBLIO
509 delete fppar->bibkey;
514 } while (par != epar);
520 // set layout over selection and make a total rebreak of those paragraphs
521 void LyXText::setLayout(BufferView * bview, string const & layout)
523 LyXCursor tmpcursor = cursor; /* store the current cursor */
525 // if there is no selection just set the layout
526 // of the current paragraph */
527 if (!selection.set()) {
528 selection.start = cursor; // dummy selection
529 selection.end = cursor;
531 Paragraph * endpar = setLayout(bview, cursor, selection.start,
532 selection.end, layout);
533 redoParagraphs(bview, selection.start, endpar);
535 // we have to reset the selection, because the
536 // geometry could have changed
537 setCursor(bview, selection.start.par(),
538 selection.start.pos(), false);
539 selection.cursor = cursor;
540 setCursor(bview, selection.end.par(), selection.end.pos(), false);
541 updateCounters(bview);
544 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
548 // increment depth over selection and
549 // make a total rebreak of those paragraphs
550 void LyXText::incDepth(BufferView * bview)
552 // If there is no selection, just use the current paragraph
553 if (!selection.set()) {
554 selection.start = cursor; // dummy selection
555 selection.end = cursor;
558 // We end at the next paragraph with depth 0
559 Paragraph * endpar = selection.end.par()->next();
561 Paragraph * undoendpar = endpar;
563 if (endpar && endpar->getDepth()) {
564 while (endpar && endpar->getDepth()) {
565 endpar = endpar->next();
569 endpar = endpar->next(); // because of parindents etc.
572 setUndo(bview, Undo::EDIT,
573 selection.start.par(), undoendpar);
575 LyXCursor tmpcursor = cursor; // store the current cursor
577 // ok we have a selection. This is always between sel_start_cursor
578 // and sel_end cursor
579 cursor = selection.start;
581 bool anything_changed = false;
584 // NOTE: you can't change the depth of a bibliography entry
585 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
586 Paragraph * prev = cursor.par()->previous();
589 if (cursor.par()->getDepth()
590 < prev->getMaxDepthAfter()) {
591 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
592 anything_changed = true;
596 if (cursor.par() == selection.end.par())
598 cursor.par(cursor.par()->next());
601 // if nothing changed set all depth to 0
602 if (!anything_changed) {
603 cursor = selection.start;
604 while (cursor.par() != selection.end.par()) {
605 cursor.par()->params().depth(0);
606 cursor.par(cursor.par()->next());
608 cursor.par()->params().depth(0);
611 redoParagraphs(bview, selection.start, endpar);
613 // we have to reset the selection, because the
614 // geometry could have changed
615 setCursor(bview, selection.start.par(), selection.start.pos());
616 selection.cursor = cursor;
617 setCursor(bview, selection.end.par(), selection.end.pos());
618 updateCounters(bview);
621 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
625 // decrement depth over selection and
626 // make a total rebreak of those paragraphs
627 void LyXText::decDepth(BufferView * bview)
629 // if there is no selection just set the layout
630 // of the current paragraph
631 if (!selection.set()) {
632 selection.start = cursor; // dummy selection
633 selection.end = cursor;
635 Paragraph * endpar = selection.end.par()->next();
636 Paragraph * undoendpar = endpar;
638 if (endpar && endpar->getDepth()) {
639 while (endpar && endpar->getDepth()) {
640 endpar = endpar->next();
644 endpar = endpar->next(); // because of parindents etc.
647 setUndo(bview, Undo::EDIT,
648 selection.start.par(), undoendpar);
650 LyXCursor tmpcursor = cursor; // store the current cursor
652 // ok we have a selection. This is always between sel_start_cursor
653 // and sel_end cursor
654 cursor = selection.start;
657 if (cursor.par()->params().depth()) {
658 cursor.par()->params()
659 .depth(cursor.par()->params().depth() - 1);
661 if (cursor.par() == selection.end.par()) {
664 cursor.par(cursor.par()->next());
667 redoParagraphs(bview, selection.start, endpar);
669 // we have to reset the selection, because the
670 // geometry could have changed
671 setCursor(bview, selection.start.par(),
672 selection.start.pos());
673 selection.cursor = cursor;
674 setCursor(bview, selection.end.par(), selection.end.pos());
675 updateCounters(bview);
678 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
682 // set font over selection and make a total rebreak of those paragraphs
683 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
685 // if there is no selection just set the current_font
686 if (!selection.set()) {
687 // Determine basis font
689 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
691 layoutfont = getLabelFont(bview->buffer(),
694 layoutfont = getLayoutFont(bview->buffer(),
697 // Update current font
698 real_current_font.update(font,
699 bview->buffer()->params.language,
702 // Reduce to implicit settings
703 current_font = real_current_font;
704 current_font.reduce(layoutfont);
705 // And resolve it completely
706 #ifndef INHERIT_LANGUAGE
707 real_current_font.realize(layoutfont);
709 real_current_font.realize(layoutfont,
710 bview->buffer()->params.language);
715 LyXCursor tmpcursor = cursor; // store the current cursor
717 // ok we have a selection. This is always between sel_start_cursor
718 // and sel_end cursor
720 setUndo(bview, Undo::EDIT,
721 selection.start.par(), selection.end.par()->next());
723 cursor = selection.start;
724 while (cursor.par() != selection.end.par() ||
725 cursor.pos() < selection.end.pos())
727 if (cursor.pos() < cursor.par()->size()) {
728 // an open footnote should behave like a closed one
729 setCharFont(bview, cursor.par(), cursor.pos(),
731 cursor.pos(cursor.pos() + 1);
734 cursor.par(cursor.par()->next());
739 redoParagraphs(bview, selection.start, selection.end.par()->next());
741 // we have to reset the selection, because the
742 // geometry could have changed, but we keep
743 // it for user convenience
744 setCursor(bview, selection.start.par(), selection.start.pos());
745 selection.cursor = cursor;
746 setCursor(bview, selection.end.par(), selection.end.pos());
748 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
749 tmpcursor.boundary());
753 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
755 Row * tmprow = cur.row();
756 int y = cur.y() - tmprow->baseline();
758 setHeightOfRow(bview, tmprow);
760 while (tmprow->previous()
761 && tmprow->previous()->par() == tmprow->par()) {
762 tmprow = tmprow->previous();
763 y -= tmprow->height();
764 setHeightOfRow(bview, tmprow);
767 // we can set the refreshing parameters now
768 status(bview, LyXText::NEED_MORE_REFRESH);
770 refresh_row = tmprow;
771 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
775 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
777 Row * tmprow = cur.row();
779 int y = cur.y() - tmprow->baseline();
780 setHeightOfRow(bview, tmprow);
782 while (tmprow->previous()
783 && tmprow->previous()->par() == tmprow->par()) {
784 tmprow = tmprow->previous();
785 y -= tmprow->height();
788 // we can set the refreshing parameters now
789 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
791 refresh_row = tmprow;
793 status(bview, LyXText::NEED_MORE_REFRESH);
794 setCursor(bview, cur.par(), cur.pos());
798 // deletes and inserts again all paragaphs between the cursor
799 // and the specified par
800 // This function is needed after SetLayout and SetFont etc.
801 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
802 Paragraph const * endpar) const
805 Paragraph * tmppar = 0;
806 Paragraph * first_phys_par = 0;
808 Row * tmprow = cur.row();
810 int y = cur.y() - tmprow->baseline();
812 if (!tmprow->previous()) {
813 // a trick/hack for UNDO
814 // This is needed because in an UNDO/REDO we could have changed
815 // the ownerParagrah() so the paragraph inside the row is NOT
816 // my really first par anymore. Got it Lars ;) (Jug 20011206)
817 first_phys_par = ownerParagraph();
819 first_phys_par = tmprow->par();
820 while (tmprow->previous()
821 && tmprow->previous()->par() == first_phys_par)
823 tmprow = tmprow->previous();
824 y -= tmprow->height();
828 // we can set the refreshing parameters now
829 status(bview, LyXText::NEED_MORE_REFRESH);
831 refresh_row = tmprow->previous(); /* the real refresh row will
832 be deleted, so I store
836 tmppar = tmprow->next()->par();
839 while (tmprow->next() && tmppar != endpar) {
840 removeRow(tmprow->next());
841 if (tmprow->next()) {
842 tmppar = tmprow->next()->par();
848 // remove the first one
849 tmprow2 = tmprow; /* this is because tmprow->previous()
851 tmprow = tmprow->previous();
854 tmppar = first_phys_par;
858 insertParagraph(bview, tmppar, tmprow);
862 while (tmprow->next()
863 && tmprow->next()->par() == tmppar) {
864 tmprow = tmprow->next();
866 tmppar = tmppar->next();
868 } while (tmppar && tmppar != endpar);
870 // this is because of layout changes
872 refresh_y -= refresh_row->height();
873 setHeightOfRow(bview, refresh_row);
875 refresh_row = firstrow;
877 setHeightOfRow(bview, refresh_row);
880 if (tmprow && tmprow->next())
881 setHeightOfRow(bview, tmprow->next());
882 updateCounters(bview);
886 void LyXText::fullRebreak(BufferView * bview)
892 if (need_break_row) {
893 breakAgain(bview, need_break_row);
900 // important for the screen
903 // the cursor set functions have a special mechanism. When they
904 // realize, that you left an empty paragraph, they will delete it.
905 // They also delete the corresponding row
907 // need the selection cursor:
908 void LyXText::setSelection(BufferView * bview)
910 bool const lsel = selection.set();
912 if (!selection.set()) {
913 last_sel_cursor = selection.cursor;
914 selection.start = selection.cursor;
915 selection.end = selection.cursor;
920 // first the toggling area
921 if (cursor.y() < last_sel_cursor.y()
922 || (cursor.y() == last_sel_cursor.y()
923 && cursor.x() < last_sel_cursor.x())) {
924 toggle_end_cursor = last_sel_cursor;
925 toggle_cursor = cursor;
927 toggle_end_cursor = cursor;
928 toggle_cursor = last_sel_cursor;
931 last_sel_cursor = cursor;
933 // and now the whole selection
935 if (selection.cursor.par() == cursor.par())
936 if (selection.cursor.pos() < cursor.pos()) {
937 selection.end = cursor;
938 selection.start = selection.cursor;
940 selection.end = selection.cursor;
941 selection.start = cursor;
943 else if (selection.cursor.y() < cursor.y() ||
944 (selection.cursor.y() == cursor.y()
945 && selection.cursor.x() < cursor.x())) {
946 selection.end = cursor;
947 selection.start = selection.cursor;
950 selection.end = selection.cursor;
951 selection.start = cursor;
954 // a selection with no contents is not a selection
955 if (selection.start.par() == selection.end.par() &&
956 selection.start.pos() == selection.end.pos())
957 selection.set(false);
959 if (inset_owner && (selection.set() || lsel))
960 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
964 string const LyXText::selectionAsString(Buffer const * buffer,
967 if (!selection.set()) return string();
969 // should be const ...
970 Paragraph * startpar(selection.start.par());
971 Paragraph * endpar(selection.end.par());
972 pos_type const startpos(selection.start.pos());
973 pos_type const endpos(selection.end.pos());
975 if (startpar == endpar) {
976 return startpar->asString(buffer, startpos, endpos, label);
981 // First paragraph in selection
982 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
984 // The paragraphs in between (if any)
985 LyXCursor tmpcur(selection.start);
986 tmpcur.par(tmpcur.par()->next());
987 while (tmpcur.par() != endpar) {
988 result += tmpcur.par()->asString(buffer, 0,
989 tmpcur.par()->size(),
991 tmpcur.par(tmpcur.par()->next());
994 // Last paragraph in selection
995 result += endpar->asString(buffer, 0, endpos, label);
1001 void LyXText::clearSelection() const
1003 selection.set(false);
1004 selection.mark(false);
1005 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1006 // reset this in the bv_owner!
1007 if (bv_owner && bv_owner->text)
1008 bv_owner->text->xsel_cache.set(false);
1012 void LyXText::cursorHome(BufferView * bview) const
1014 setCursor(bview, cursor.par(), cursor.row()->pos());
1018 void LyXText::cursorEnd(BufferView * bview) const
1020 if (!cursor.row()->next()
1021 || cursor.row()->next()->par() != cursor.row()->par()) {
1022 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1024 if (!cursor.par()->empty() &&
1025 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1026 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1027 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1029 setCursor(bview,cursor.par(),
1030 rowLast(cursor.row()) + 1);
1036 void LyXText::cursorTop(BufferView * bview) const
1038 while (cursor.par()->previous())
1039 cursor.par(cursor.par()->previous());
1040 setCursor(bview, cursor.par(), 0);
1044 void LyXText::cursorBottom(BufferView * bview) const
1046 while (cursor.par()->next())
1047 cursor.par(cursor.par()->next());
1048 setCursor(bview, cursor.par(), cursor.par()->size());
1052 void LyXText::toggleFree(BufferView * bview,
1053 LyXFont const & font, bool toggleall)
1055 // If the mask is completely neutral, tell user
1056 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1057 // Could only happen with user style
1058 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1062 // Try implicit word selection
1063 // If there is a change in the language the implicit word selection
1065 LyXCursor resetCursor = cursor;
1066 bool implicitSelection = (font.language() == ignore_language
1067 && font.number() == LyXFont::IGNORE)
1068 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1071 setFont(bview, font, toggleall);
1073 // Implicit selections are cleared afterwards
1074 //and cursor is set to the original position.
1075 if (implicitSelection) {
1077 cursor = resetCursor;
1078 setCursor(bview, cursor.par(), cursor.pos());
1079 selection.cursor = cursor;
1082 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1086 string LyXText::getStringToIndex(BufferView * bview)
1090 // Try implicit word selection
1091 // If there is a change in the language the implicit word selection
1093 LyXCursor const reset_cursor = cursor;
1094 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1096 if (!selection.set()) {
1097 bview->owner()->message(_("Nothing to index!"));
1100 if (selection.start.par() != selection.end.par()) {
1101 bview->owner()->message(_("Cannot index more than one paragraph!"));
1105 idxstring = selectionAsString(bview->buffer(), false);
1107 // Implicit selections are cleared afterwards
1108 //and cursor is set to the original position.
1109 if (implicitSelection) {
1111 cursor = reset_cursor;
1112 setCursor(bview, cursor.par(), cursor.pos());
1113 selection.cursor = cursor;
1119 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1120 Paragraph const * par) const
1122 if (par->layout()->labeltype != LABEL_MANUAL)
1125 return par->beginningOfMainBody();
1129 // the DTP switches for paragraphs. LyX will store them in the first
1130 // physicla paragraph. When a paragraph is broken, the top settings rest,
1131 // the bottom settings are given to the new one. So I can make shure,
1132 // they do not duplicate themself and you cannnot make dirty things with
1135 void LyXText::setParagraph(BufferView * bview,
1136 bool line_top, bool line_bottom,
1137 bool pagebreak_top, bool pagebreak_bottom,
1138 VSpace const & space_top,
1139 VSpace const & space_bottom,
1140 Spacing const & spacing,
1142 string labelwidthstring,
1145 LyXCursor tmpcursor = cursor;
1146 if (!selection.set()) {
1147 selection.start = cursor;
1148 selection.end = cursor;
1151 // make sure that the depth behind the selection are restored, too
1152 Paragraph * endpar = selection.end.par()->next();
1153 Paragraph * undoendpar = endpar;
1155 if (endpar && endpar->getDepth()) {
1156 while (endpar && endpar->getDepth()) {
1157 endpar = endpar->next();
1158 undoendpar = endpar;
1162 // because of parindents etc.
1163 endpar = endpar->next();
1166 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1169 Paragraph * tmppar = selection.end.par();
1171 while (tmppar != selection.start.par()->previous()) {
1172 setCursor(bview, tmppar, 0);
1173 status(bview, LyXText::NEED_MORE_REFRESH);
1174 refresh_row = cursor.row();
1175 refresh_y = cursor.y() - cursor.row()->baseline();
1176 cursor.par()->params().lineTop(line_top);
1177 cursor.par()->params().lineBottom(line_bottom);
1178 cursor.par()->params().pagebreakTop(pagebreak_top);
1179 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1180 cursor.par()->params().spaceTop(space_top);
1181 cursor.par()->params().spaceBottom(space_bottom);
1182 cursor.par()->params().spacing(spacing);
1183 // does the layout allow the new alignment?
1184 LyXLayout_ptr const & layout = cursor.par()->layout();
1186 if (align == LYX_ALIGN_LAYOUT)
1187 align = layout->align;
1188 if (align & layout->alignpossible) {
1189 if (align == layout->align)
1190 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1192 cursor.par()->params().align(align);
1194 cursor.par()->setLabelWidthString(labelwidthstring);
1195 cursor.par()->params().noindent(noindent);
1196 tmppar = cursor.par()->previous();
1199 redoParagraphs(bview, selection.start, endpar);
1202 setCursor(bview, selection.start.par(), selection.start.pos());
1203 selection.cursor = cursor;
1204 setCursor(bview, selection.end.par(), selection.end.pos());
1205 setSelection(bview);
1206 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1208 bview->updateInset(inset_owner, true);
1212 // set the counter of a paragraph. This includes the labels
1213 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1215 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1216 LyXLayout_ptr const & layout = par->layout();
1218 if (par->previous()) {
1220 par->params().appendix(par->previous()->params().appendix());
1221 if (!par->params().appendix() && par->params().startOfAppendix()) {
1222 par->params().appendix(true);
1223 buf->counters().reset();
1225 par->enumdepth = par->previous()->enumdepth;
1226 par->itemdepth = par->previous()->itemdepth;
1228 par->params().appendix(par->params().startOfAppendix());
1233 /* Maybe we have to increment the enumeration depth.
1234 * BUT, enumeration in a footnote is considered in isolation from its
1235 * surrounding paragraph so don't increment if this is the
1236 * first line of the footnote
1237 * AND, bibliographies can't have their depth changed ie. they
1238 * are always of depth 0
1241 && par->previous()->getDepth() < par->getDepth()
1242 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1243 && par->enumdepth < 3
1244 && layout->labeltype != LABEL_BIBLIO) {
1248 // Maybe we have to decrement the enumeration depth, see note above
1250 && par->previous()->getDepth() > par->getDepth()
1251 && layout->labeltype != LABEL_BIBLIO) {
1252 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1255 if (!par->params().labelString().empty()) {
1256 par->params().labelString(string());
1259 if (layout->margintype == MARGIN_MANUAL) {
1260 if (par->params().labelWidthString().empty()) {
1261 par->setLabelWidthString(layout->labelstring());
1264 par->setLabelWidthString(string());
1267 // is it a layout that has an automatic label?
1268 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1270 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1271 string numbertype, langtype;
1274 if (i >= 0 && i <= buf->params.secnumdepth) {
1276 buf->counters().step(buf->counters().sects[i]);
1278 // Is there a label? Useful for Chapter layout
1279 if (!par->params().appendix()) {
1280 if (!layout->labelstring().empty())
1281 par->params().labelString(layout->labelstring());
1283 par->params().labelString(string());
1285 if (!layout->labelstring_appendix().empty())
1286 par->params().labelString(layout->labelstring_appendix());
1288 par->params().labelString(string());
1291 // Use if an integer is here less than elegant. For now.
1292 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1293 if (!par->params().appendix()) {
1294 numbertype = "sectioning";
1296 numbertype = "appendix";
1297 if (par->isRightToLeftPar(buf->params))
1298 langtype = "hebrew";
1303 s << buf->counters().numberLabel(buf->counters().sects[i],
1304 numbertype, langtype, head);
1306 par->params().labelString(par->params().labelString() + s.str().c_str());
1307 // We really want to remove the c_str as soon as
1310 // reset enum counters
1311 buf->counters().reset("enum");
1312 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1313 buf->counters().reset("enum");
1314 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1315 buf->counters().step(buf->counters().enums[par->enumdepth]);
1317 s << buf->counters().numberLabel(buf->counters().enums[par->enumdepth],
1318 "enumeration", langtype);
1319 par->params().labelString(s.str().c_str());
1322 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1323 buf->counters().step("bibitem");
1324 int number = buf->counters().value("bibitem");
1326 InsetCommandParams p("bibitem" );
1327 par->bibkey = new InsetBibKey(p);
1329 par->bibkey->setCounter(number);
1330 par->params().labelString(layout->labelstring());
1332 // In biblio should't be following counters but...
1334 string s = layout->labelstring();
1336 // the caption hack:
1337 if (layout->labeltype == LABEL_SENSITIVE) {
1338 Paragraph * tmppar = par;
1341 while (tmppar && tmppar->inInset()
1342 // the single '=' is intended below
1343 && (in = tmppar->inInset()->owner())) {
1344 if (in->lyxCode() == Inset::FLOAT_CODE) {
1348 tmppar = in->parOwner();
1354 = floatList.getType(static_cast<InsetFloat*>(in)->type());
1356 buf->counters().step(fl.name());
1358 // Doesn't work... yet.
1360 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1361 o << fl.name() << " #:";
1364 // par->SetLayout(0);
1365 // s = layout->labelstring;
1366 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1367 ? " :úåòîùî øñç" : "Senseless: ";
1370 par->params().labelString(s);
1372 // reset the enumeration counter. They are always resetted
1373 // when there is any other layout between
1374 for (int i = par->enumdepth; i < 4; ++i) {
1375 buf->counters().set(buf->counters().enums[i], 0);
1381 // Updates all counters BEHIND the row. Changed paragraphs
1382 // with a dynamic left margin will be rebroken.
1383 void LyXText::updateCounters(BufferView * bview) const
1387 Row * row = firstrow;
1390 bview->buffer()->counters().reset();
1392 while (row->par() != par)
1395 setCounter(bview->buffer(), par);
1397 // now check for the headline layouts. remember that they
1398 // have a dynamic left margin
1399 LyXLayout_ptr const & layout = par->layout();
1401 if (layout->margintype == MARGIN_DYNAMIC
1402 || layout->labeltype == LABEL_SENSITIVE) {
1403 // Rebreak the paragraph
1404 removeParagraph(row);
1405 appendParagraph(bview, row);
1412 void LyXText::insertInset(BufferView * bview, Inset * inset)
1414 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1416 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1418 cursor.par()->insertInset(cursor.pos(), inset);
1419 // Just to rebreak and refresh correctly.
1420 // The character will not be inserted a second time
1421 insertChar(bview, Paragraph::META_INSET);
1422 // If we enter a highly editable inset the cursor should be to before
1423 // the inset. This couldn't happen before as Undo was not handled inside
1424 // inset now after the Undo LyX tries to call inset->Edit(...) again
1425 // and cannot do this as the cursor is behind the inset and GetInset
1426 // does not return the inset!
1427 if (isHighlyEditableInset(inset)) {
1428 cursorLeft(bview, true);
1434 void LyXText::copyEnvironmentType()
1436 copylayouttype = cursor.par()->layout()->name();
1440 void LyXText::pasteEnvironmentType(BufferView * bview)
1442 setLayout(bview, copylayouttype);
1446 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1448 // Stuff what we got on the clipboard. Even if there is no selection.
1450 // There is a problem with having the stuffing here in that the
1451 // larger the selection the slower LyX will get. This can be
1452 // solved by running the line below only when the selection has
1453 // finished. The solution used currently just works, to make it
1454 // faster we need to be more clever and probably also have more
1455 // calls to stuffClipboard. (Lgb)
1456 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1458 // This doesn't make sense, if there is no selection
1459 if (!selection.set())
1462 // OK, we have a selection. This is always between selection.start
1463 // and selection.end
1465 // make sure that the depth behind the selection are restored, too
1466 Paragraph * endpar = selection.end.par()->next();
1467 Paragraph * undoendpar = endpar;
1469 if (endpar && endpar->getDepth()) {
1470 while (endpar && endpar->getDepth()) {
1471 endpar = endpar->next();
1472 undoendpar = endpar;
1474 } else if (endpar) {
1475 endpar = endpar->next(); // because of parindents etc.
1478 setUndo(bview, Undo::DELETE,
1479 selection.start.par(), undoendpar);
1481 // there are two cases: cut only within one paragraph or
1482 // more than one paragraph
1483 if (selection.start.par() == selection.end.par()) {
1484 // only within one paragraph
1485 endpar = selection.end.par();
1486 int pos = selection.end.pos();
1487 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1488 selection.start.pos(), pos,
1489 bview->buffer()->params.textclass,
1491 selection.end.pos(pos);
1493 endpar = selection.end.par();
1494 int pos = selection.end.pos();
1495 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1496 selection.start.pos(), pos,
1497 bview->buffer()->params.textclass,
1500 selection.end.par(endpar);
1501 selection.end.pos(pos);
1502 cursor.pos(selection.end.pos());
1504 endpar = endpar->next();
1506 // sometimes necessary
1508 selection.start.par()->stripLeadingSpaces();
1510 redoParagraphs(bview, selection.start, endpar);
1512 // cutSelection can invalidate the cursor so we need to set
1514 cursor = selection.start;
1516 // need a valid cursor. (Lgb)
1519 setCursor(bview, cursor.par(), cursor.pos());
1520 selection.cursor = cursor;
1521 updateCounters(bview);
1525 void LyXText::copySelection(BufferView * bview)
1527 // stuff the selection onto the X clipboard, from an explicit copy request
1528 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1530 // this doesnt make sense, if there is no selection
1531 if (!selection.set())
1534 // ok we have a selection. This is always between selection.start
1535 // and sel_end cursor
1537 // copy behind a space if there is one
1538 while (selection.start.par()->size() > selection.start.pos()
1539 && selection.start.par()->isLineSeparator(selection.start.pos())
1540 && (selection.start.par() != selection.end.par()
1541 || selection.start.pos() < selection.end.pos()))
1542 selection.start.pos(selection.start.pos() + 1);
1544 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1545 selection.start.pos(), selection.end.pos(),
1546 bview->buffer()->params.textclass);
1550 void LyXText::pasteSelection(BufferView * bview)
1552 // this does not make sense, if there is nothing to paste
1553 if (!CutAndPaste::checkPastePossible(cursor.par()))
1556 setUndo(bview, Undo::INSERT,
1557 cursor.par(), cursor.par()->next());
1560 Paragraph * actpar = cursor.par();
1561 int pos = cursor.pos();
1563 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1564 bview->buffer()->params.textclass);
1566 redoParagraphs(bview, cursor, endpar);
1568 setCursor(bview, cursor.par(), cursor.pos());
1571 selection.cursor = cursor;
1572 setCursor(bview, actpar, pos);
1573 setSelection(bview);
1574 updateCounters(bview);
1578 // sets the selection over the number of characters of string, no check!!
1579 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1584 selection.cursor = cursor;
1585 for (string::size_type i = 0; i < str.length(); ++i)
1587 setSelection(bview);
1591 // simple replacing. The font of the first selected character is used
1592 void LyXText::replaceSelectionWithString(BufferView * bview,
1595 setCursorParUndo(bview);
1598 if (!selection.set()) { // create a dummy selection
1599 selection.end = cursor;
1600 selection.start = cursor;
1603 // Get font setting before we cut
1604 pos_type pos = selection.end.pos();
1605 LyXFont const font = selection.start.par()
1606 ->getFontSettings(bview->buffer()->params,
1607 selection.start.pos());
1609 // Insert the new string
1610 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1611 selection.end.par()->insertChar(pos, (*cit), font);
1615 // Cut the selection
1616 cutSelection(bview, true, false);
1622 // needed to insert the selection
1623 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1625 Paragraph * par = cursor.par();
1626 pos_type pos = cursor.pos();
1627 Paragraph * endpar = cursor.par()->next();
1629 setCursorParUndo(bview);
1631 // only to be sure, should not be neccessary
1634 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1636 redoParagraphs(bview, cursor, endpar);
1637 setCursor(bview, cursor.par(), cursor.pos());
1638 selection.cursor = cursor;
1639 setCursor(bview, par, pos);
1640 setSelection(bview);
1644 // turns double-CR to single CR, others where converted into one
1645 // blank. Then InsertStringAsLines is called
1646 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1648 string linestr(str);
1649 bool newline_inserted = false;
1650 for (string::size_type i = 0; i < linestr.length(); ++i) {
1651 if (linestr[i] == '\n') {
1652 if (newline_inserted) {
1653 // we know that \r will be ignored by
1654 // InsertStringA. Of course, it is a dirty
1655 // trick, but it works...
1656 linestr[i - 1] = '\r';
1660 newline_inserted = true;
1662 } else if (IsPrintable(linestr[i])) {
1663 newline_inserted = false;
1666 insertStringAsLines(bview, linestr);
1670 bool LyXText::gotoNextInset(BufferView * bview,
1671 vector<Inset::Code> const & codes,
1672 string const & contents) const
1674 LyXCursor res = cursor;
1677 if (res.pos() < res.par()->size() - 1) {
1678 res.pos(res.pos() + 1);
1680 res.par(res.par()->next());
1684 } while (res.par() &&
1685 !(res.par()->isInset(res.pos())
1686 && (inset = res.par()->getInset(res.pos())) != 0
1687 && find(codes.begin(), codes.end(), inset->lyxCode())
1689 && (contents.empty() ||
1690 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1694 setCursor(bview, res.par(), res.pos(), false);
1701 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1704 LyXCursor tmpcursor;
1708 Row * row = getRow(par, pos, y);
1710 // is there a break one row above
1711 if (row->previous() && row->previous()->par() == row->par()) {
1712 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1713 if (z >= row->pos()) {
1714 // set the dimensions of the row above
1715 y -= row->previous()->height();
1717 refresh_row = row->previous();
1718 status(bview, LyXText::NEED_MORE_REFRESH);
1720 breakAgain(bview, row->previous());
1722 // set the cursor again. Otherwise
1723 // dangling pointers are possible
1724 setCursor(bview, cursor.par(), cursor.pos(),
1725 false, cursor.boundary());
1726 selection.cursor = cursor;
1731 int const tmpheight = row->height();
1732 pos_type const tmplast = rowLast(row);
1736 breakAgain(bview, row);
1737 if (row->height() == tmpheight && rowLast(row) == tmplast)
1738 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1740 status(bview, LyXText::NEED_MORE_REFRESH);
1742 // check the special right address boxes
1743 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1750 redoDrawingOfParagraph(bview, tmpcursor);
1753 // set the cursor again. Otherwise dangling pointers are possible
1754 // also set the selection
1756 if (selection.set()) {
1758 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1759 false, selection.cursor.boundary());
1760 selection.cursor = cursor;
1761 setCursorIntern(bview, selection.start.par(),
1762 selection.start.pos(),
1763 false, selection.start.boundary());
1764 selection.start = cursor;
1765 setCursorIntern(bview, selection.end.par(),
1766 selection.end.pos(),
1767 false, selection.end.boundary());
1768 selection.end = cursor;
1769 setCursorIntern(bview, last_sel_cursor.par(),
1770 last_sel_cursor.pos(),
1771 false, last_sel_cursor.boundary());
1772 last_sel_cursor = cursor;
1775 setCursorIntern(bview, cursor.par(), cursor.pos(),
1776 false, cursor.boundary());
1780 // returns false if inset wasn't found
1781 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1783 // first check the current paragraph
1784 int pos = cursor.par()->getPositionOfInset(inset);
1786 checkParagraph(bview, cursor.par(), pos);
1790 // check every paragraph
1792 Paragraph * par = ownerParagraph();
1794 pos = par->getPositionOfInset(inset);
1796 checkParagraph(bview, par, pos);
1806 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1808 bool setfont, bool boundary) const
1810 LyXCursor old_cursor = cursor;
1811 setCursorIntern(bview, par, pos, setfont, boundary);
1812 return deleteEmptyParagraphMechanism(bview, old_cursor);
1816 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1817 pos_type pos, bool boundary) const
1824 cur.boundary(boundary);
1826 // get the cursor y position in text
1828 Row * row = getRow(par, pos, y);
1829 Row * old_row = row;
1831 // if we are before the first char of this row and are still in the
1832 // same paragraph and there is a previous row then put the cursor on
1833 // the end of the previous row
1834 cur.iy(y + row->baseline());
1836 if (row->previous() && pos &&
1837 row->previous()->par() == row->par() &&
1838 par->getChar(pos) == Paragraph::META_INSET &&
1839 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1841 row = row->previous();
1846 // y is now the beginning of the cursor row
1847 y += row->baseline();
1848 // y is now the cursor baseline
1851 pos_type last = rowLastPrintable(old_row);
1853 if (pos > last + 1) {
1854 // This shouldn't happen.
1857 } else if (pos < row->pos()) {
1862 // now get the cursors x position
1863 float x = getCursorX(bview, row, pos, last, boundary);
1866 if (old_row != row) {
1867 x = getCursorX(bview, old_row, pos, last, boundary);
1874 float LyXText::getCursorX(BufferView * bview, Row * row,
1875 pos_type pos, pos_type last, bool boundary) const
1877 pos_type cursor_vpos = 0;
1879 float fill_separator;
1881 float fill_label_hfill;
1882 // This call HAS to be here because of the BidiTables!!!
1883 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1886 if (last < row->pos())
1887 cursor_vpos = row->pos();
1888 else if (pos > last && !boundary)
1889 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1890 ? row->pos() : last + 1;
1891 else if (pos > row->pos() &&
1892 (pos > last || boundary))
1893 /// Place cursor after char at (logical) position pos - 1
1894 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1895 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1897 /// Place cursor before char at (logical) position pos
1898 cursor_vpos = (bidi_level(pos) % 2 == 0)
1899 ? log2vis(pos) : log2vis(pos) + 1;
1901 pos_type main_body =
1902 beginningOfMainBody(bview->buffer(), row->par());
1903 if ((main_body > 0) &&
1904 ((main_body-1 > last) ||
1905 !row->par()->isLineSeparator(main_body-1)))
1908 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1909 pos_type pos = vis2log(vpos);
1910 if (main_body > 0 && pos == main_body - 1) {
1911 x += fill_label_hfill +
1912 font_metrics::width(
1913 row->par()->layout()->labelsep,
1914 getLabelFont(bview->buffer(),
1916 if (row->par()->isLineSeparator(main_body - 1))
1917 x -= singleWidth(bview,
1918 row->par(), main_body - 1);
1920 if (hfillExpansion(bview->buffer(), row, pos)) {
1921 x += singleWidth(bview, row->par(), pos);
1922 if (pos >= main_body)
1925 x += fill_label_hfill;
1926 } else if (row->par()->isSeparator(pos)) {
1927 x += singleWidth(bview, row->par(), pos);
1928 if (pos >= main_body)
1929 x += fill_separator;
1931 x += singleWidth(bview, row->par(), pos);
1937 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1938 pos_type pos, bool setfont, bool boundary) const
1940 InsetText * it = static_cast<InsetText *>(par->inInset());
1942 if (it != inset_owner) {
1943 lyxerr[Debug::INSETS] << "InsetText is " << it
1945 << "inset_owner is "
1946 << inset_owner << endl;
1947 #ifdef WITH_WARNINGS
1948 #warning I believe this code is wrong. (Lgb)
1949 #warning Jürgen, have a look at this. (Lgb)
1950 #warning Hmmm, I guess you are right but we
1951 #warning should verify when this is needed
1953 // Jürgen, would you like to have a look?
1954 // I guess we need to move the outer cursor
1955 // and open and lock the inset (bla bla bla)
1956 // stuff I don't know... so can you have a look?
1958 // I moved the lyxerr stuff in here so we can see if
1959 // this is actually really needed and where!
1961 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1966 setCursor(bview, cursor, par, pos, boundary);
1968 setCurrentFont(bview);
1972 void LyXText::setCurrentFont(BufferView * bview) const
1974 pos_type pos = cursor.pos();
1975 if (cursor.boundary() && pos > 0)
1979 if (pos == cursor.par()->size())
1981 else // potentional bug... BUG (Lgb)
1982 if (cursor.par()->isSeparator(pos)) {
1983 if (pos > cursor.row()->pos() &&
1984 bidi_level(pos) % 2 ==
1985 bidi_level(pos - 1) % 2)
1987 else if (pos + 1 < cursor.par()->size())
1993 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1994 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1996 if (cursor.pos() == cursor.par()->size() &&
1997 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1998 !cursor.boundary()) {
1999 Language const * lang =
2000 cursor.par()->getParLanguage(bview->buffer()->params);
2001 current_font.setLanguage(lang);
2002 current_font.setNumber(LyXFont::OFF);
2003 real_current_font.setLanguage(lang);
2004 real_current_font.setNumber(LyXFont::OFF);
2009 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2011 LyXCursor old_cursor = cursor;
2013 setCursorFromCoordinates(bview, cursor, x, y);
2014 setCurrentFont(bview);
2015 deleteEmptyParagraphMechanism(bview, old_cursor);
2022 * return true if the cursor given is at the end of a row,
2023 * and the next row is filled by an inset that spans an entire
2026 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2029 Row const & next = *row.next();
2031 if (next.pos() != cur.pos() || next.par() != cur.par())
2033 if (!cur.par()->isInset(cur.pos()))
2035 Inset const * inset = cur.par()->getInset(cur.pos());
2036 if (inset->needFullRow() || inset->display())
2043 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2046 // Get the row first.
2048 Row * row = getRowNearY(y);
2050 pos_type const column = getColumnNearX(bview, row, x, bound);
2051 cur.par(row->par());
2052 cur.pos(row->pos() + column);
2054 cur.y(y + row->baseline());
2057 if (beforeFullRowInset(*row, cur)) {
2058 pos_type last = rowLastPrintable(row);
2059 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2061 cur.iy(y + row->height() + row->next()->baseline());
2062 cur.irow(row->next());
2068 cur.boundary(bound);
2072 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2074 if (cursor.pos() > 0) {
2075 bool boundary = cursor.boundary();
2076 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2077 if (!internal && !boundary &&
2078 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2079 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2080 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2081 Paragraph * par = cursor.par()->previous();
2082 setCursor(bview, par, par->size());
2087 void LyXText::cursorRight(BufferView * bview, bool internal) const
2089 if (!internal && cursor.boundary() &&
2090 !cursor.par()->isNewline(cursor.pos()))
2091 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2092 else if (cursor.pos() < cursor.par()->size()) {
2093 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2095 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2096 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2097 } else if (cursor.par()->next())
2098 setCursor(bview, cursor.par()->next(), 0);
2102 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2105 int x = cursor.x_fix();
2106 int y = cursor.y() - cursor.row()->baseline() - 1;
2107 setCursorFromCoordinates(bview, x, y);
2109 int y1 = cursor.iy() - first_y;
2113 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2114 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2115 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2119 setCursorFromCoordinates(bview, cursor.x_fix(),
2120 cursor.y() - cursor.row()->baseline() - 1);
2125 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2128 int x = cursor.x_fix();
2129 int y = cursor.y() - cursor.row()->baseline() +
2130 cursor.row()->height() + 1;
2131 setCursorFromCoordinates(bview, x, y);
2132 if (!selecting && cursor.row() == cursor.irow()) {
2133 int y1 = cursor.iy() - first_y;
2137 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2138 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2139 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2143 setCursorFromCoordinates(bview, cursor.x_fix(),
2144 cursor.y() - cursor.row()->baseline()
2145 + cursor.row()->height() + 1);
2150 void LyXText::cursorUpParagraph(BufferView * bview) const
2152 if (cursor.pos() > 0) {
2153 setCursor(bview, cursor.par(), 0);
2155 else if (cursor.par()->previous()) {
2156 setCursor(bview, cursor.par()->previous(), 0);
2161 void LyXText::cursorDownParagraph(BufferView * bview) const
2163 if (cursor.par()->next()) {
2164 setCursor(bview, cursor.par()->next(), 0);
2166 setCursor(bview, cursor.par(), cursor.par()->size());
2170 // fix the cursor `cur' after a characters has been deleted at `where'
2171 // position. Called by deleteEmptyParagraphMechanism
2172 void LyXText::fixCursorAfterDelete(BufferView * bview,
2174 LyXCursor const & where) const
2176 // if cursor is not in the paragraph where the delete occured,
2178 if (cur.par() != where.par())
2181 // if cursor position is after the place where the delete occured,
2183 if (cur.pos() > where.pos())
2184 cur.pos(cur.pos()-1);
2186 // check also if we don't want to set the cursor on a spot behind the
2187 // pagragraph because we erased the last character.
2188 if (cur.pos() > cur.par()->size())
2189 cur.pos(cur.par()->size());
2191 // recompute row et al. for this cursor
2192 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2196 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2197 LyXCursor const & old_cursor) const
2199 // Would be wrong to delete anything if we have a selection.
2200 if (selection.set())
2203 // We allow all kinds of "mumbo-jumbo" when freespacing.
2204 if (old_cursor.par()->layout()->free_spacing
2205 || old_cursor.par()->isFreeSpacing()) {
2209 /* Ok I'll put some comments here about what is missing.
2210 I have fixed BackSpace (and thus Delete) to not delete
2211 double-spaces automagically. I have also changed Cut,
2212 Copy and Paste to hopefully do some sensible things.
2213 There are still some small problems that can lead to
2214 double spaces stored in the document file or space at
2215 the beginning of paragraphs. This happens if you have
2216 the cursor betwenn to spaces and then save. Or if you
2217 cut and paste and the selection have a space at the
2218 beginning and then save right after the paste. I am
2219 sure none of these are very hard to fix, but I will
2220 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2221 that I can get some feedback. (Lgb)
2224 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2225 // delete the LineSeparator.
2228 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2229 // delete the LineSeparator.
2232 // If the pos around the old_cursor were spaces, delete one of them.
2233 if (old_cursor.par() != cursor.par()
2234 || old_cursor.pos() != cursor.pos()) {
2235 // Only if the cursor has really moved
2237 if (old_cursor.pos() > 0
2238 && old_cursor.pos() < old_cursor.par()->size()
2239 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2240 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2241 old_cursor.par()->erase(old_cursor.pos() - 1);
2242 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2244 #ifdef WITH_WARNINGS
2245 #warning This will not work anymore when we have multiple views of the same buffer
2246 // In this case, we will have to correct also the cursors held by
2247 // other bufferviews. It will probably be easier to do that in a more
2248 // automated way in LyXCursor code. (JMarc 26/09/2001)
2250 // correct all cursors held by the LyXText
2251 fixCursorAfterDelete(bview, cursor, old_cursor);
2252 fixCursorAfterDelete(bview, selection.cursor,
2254 fixCursorAfterDelete(bview, selection.start,
2256 fixCursorAfterDelete(bview, selection.end, old_cursor);
2257 fixCursorAfterDelete(bview, last_sel_cursor,
2259 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2260 fixCursorAfterDelete(bview, toggle_end_cursor,
2266 // don't delete anything if this is the ONLY paragraph!
2267 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2270 // Do not delete empty paragraphs with keepempty set.
2271 if (old_cursor.par()->layout()->keepempty)
2274 // only do our magic if we changed paragraph
2275 if (old_cursor.par() == cursor.par())
2278 // record if we have deleted a paragraph
2279 // we can't possibly have deleted a paragraph before this point
2280 bool deleted = false;
2282 if ((old_cursor.par()->empty()
2283 || (old_cursor.par()->size() == 1
2284 && old_cursor.par()->isLineSeparator(0)))) {
2285 // ok, we will delete anything
2286 LyXCursor tmpcursor;
2288 // make sure that you do not delete any environments
2289 status(bview, LyXText::NEED_MORE_REFRESH);
2292 if (old_cursor.row()->previous()) {
2293 refresh_row = old_cursor.row()->previous();
2294 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2296 cursor = old_cursor; // that undo can restore the right cursor position
2297 Paragraph * endpar = old_cursor.par()->next();
2298 if (endpar && endpar->getDepth()) {
2299 while (endpar && endpar->getDepth()) {
2300 endpar = endpar->next();
2303 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2307 removeRow(old_cursor.row());
2308 if (ownerParagraph() == old_cursor.par()) {
2309 ownerParagraph(ownerParagraph()->next());
2312 delete old_cursor.par();
2314 /* Breakagain the next par. Needed because of
2315 * the parindent that can occur or dissappear.
2316 * The next row can change its height, if
2317 * there is another layout before */
2318 if (refresh_row->next()) {
2319 breakAgain(bview, refresh_row->next());
2320 updateCounters(bview);
2322 setHeightOfRow(bview, refresh_row);
2324 refresh_row = old_cursor.row()->next();
2325 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2328 cursor = old_cursor; // that undo can restore the right cursor position
2329 Paragraph * endpar = old_cursor.par()->next();
2330 if (endpar && endpar->getDepth()) {
2331 while (endpar && endpar->getDepth()) {
2332 endpar = endpar->next();
2335 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2339 removeRow(old_cursor.row());
2341 if (ownerParagraph() == old_cursor.par()) {
2342 ownerParagraph(ownerParagraph()->next());
2345 delete old_cursor.par();
2347 /* Breakagain the next par. Needed because of
2348 the parindent that can occur or dissappear.
2349 The next row can change its height, if
2350 there is another layout before */
2352 breakAgain(bview, refresh_row);
2353 updateCounters(bview);
2358 setCursorIntern(bview, cursor.par(), cursor.pos());
2360 if (selection.cursor.par() == old_cursor.par()
2361 && selection.cursor.pos() == old_cursor.pos()) {
2362 // correct selection
2363 selection.cursor = cursor;
2367 if (old_cursor.par()->stripLeadingSpaces()) {
2368 redoParagraphs(bview, old_cursor,
2369 old_cursor.par()->next());
2371 setCursorIntern(bview, cursor.par(), cursor.pos());
2372 selection.cursor = cursor;
2379 Paragraph * LyXText::ownerParagraph() const
2382 return inset_owner->paragraph();
2384 return &*(bv_owner->buffer()->paragraphs.begin());
2388 void LyXText::ownerParagraph(Paragraph * p) const
2391 inset_owner->paragraph(p);
2393 bv_owner->buffer()->paragraphs.set(p);
2398 void LyXText::ownerParagraph(int id, Paragraph * p) const
2400 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2401 if (op && op->inInset()) {
2402 static_cast<InsetText *>(op->inInset())->paragraph(p);
2409 LyXText::text_status LyXText::status() const
2415 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2417 LyXText * t = bview->text;
2419 // We should only go up with refreshing code so this means that if
2420 // we have a MORE refresh we should never set it to LITTLE if we still
2421 // didn't handle it (and then it will be UNCHANGED. Now as long as
2422 // we stay inside one LyXText this may work but we need to tell the
2423 // outermost LyXText that it should REALLY draw us if there is some
2424 // change in a Inset::LyXText. So you see that when we are inside a
2425 // inset's LyXText we give the LITTLE to the outermost LyXText to
2426 // tell'em that it should redraw the actual row (where the inset
2427 // resides! Capito?!
2429 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2431 if (inset_owner && st != UNCHANGED) {
2432 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2433 if (!t->refresh_row) {
2434 t->refresh_row = t->cursor.row();
2435 t->refresh_y = t->cursor.y() -
2436 t->cursor.row()->baseline();