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 * ====================================================== */
15 #include "paragraph.h"
16 #include "frontends/LyXView.h"
17 #include "undo_funcs.h"
19 #include "bufferparams.h"
21 #include "BufferView.h"
22 #include "CutAndPaste.h"
23 #include "frontends/Painter.h"
24 #include "frontends/font_metrics.h"
28 #include "FloatList.h"
30 #include "ParagraphParameters.h"
33 #include "insets/inseterror.h"
34 #include "insets/insetbibitem.h"
35 #include "insets/insetspecialchar.h"
36 #include "insets/insettext.h"
37 #include "insets/insetfloat.h"
38 #include "insets/insetwrap.h"
40 #include "support/LAssert.h"
41 #include "support/textutils.h"
42 #include "support/lstrings.h"
44 #include "BoostFormat.h"
54 LyXText::LyXText(BufferView * bv)
55 : height(0), width(0), top_row_(0), top_row_offset_(0),
56 inset_owner(0), the_locking_inset(0), need_break_row(0),
57 bv_owner(bv), firstrow(0), lastrow(0)
63 LyXText::LyXText(InsetText * inset)
64 : height(0), width(0), top_row_(0), top_row_offset_(0),
65 inset_owner(inset), the_locking_inset(0), need_break_row(0),
66 bv_owner(0), firstrow(0), lastrow(0)
72 void LyXText::init(BufferView * bview, bool reinit)
75 // Delete all rows, this does not touch the paragraphs!
76 Row * tmprow = firstrow;
78 tmprow = firstrow->next();
86 copylayouttype.erase();
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;
102 updateCounters(bview);
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next();
120 LyXFont const realizeFont(LyXFont const & font,
124 LyXTextClass const & tclass = buf->params.getLyXTextClass();
125 LyXFont tmpfont(font);
126 Paragraph::depth_type par_depth = par->getDepth();
128 // Resolve against environment font information
129 while (par && par_depth && !tmpfont.resolved()) {
130 par = par->outerHook();
132 tmpfont.realize(par->layout()->font);
133 par_depth = par->getDepth();
137 tmpfont.realize(tclass.defaultfont());
145 // Gets the fully instantiated font at a given position in a paragraph
146 // Basically the same routine as Paragraph::getFont() in paragraph.C.
147 // The difference is that this one is used for displaying, and thus we
148 // are allowed to make cosmetic improvements. For instance make footnotes
150 // If position is -1, we get the layout font of the paragraph.
151 // If position is -2, we get the font of the manual label of the paragraph.
152 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
155 lyx::Assert(pos >= 0);
157 LyXLayout_ptr const & layout = par->layout();
159 // We specialize the 95% common case:
160 if (!par->getDepth()) {
161 if (layout->labeltype == LABEL_MANUAL
162 && pos < par->beginningOfBody()) {
164 LyXFont f = par->getFontSettings(buf->params, pos);
166 par->inInset()->getDrawFont(f);
167 return f.realize(layout->reslabelfont);
169 LyXFont f = par->getFontSettings(buf->params, pos);
171 par->inInset()->getDrawFont(f);
172 return f.realize(layout->resfont);
176 // The uncommon case need not be optimized as much
180 if (pos < par->beginningOfBody()) {
182 layoutfont = layout->labelfont;
185 layoutfont = layout->font;
188 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
189 tmpfont.realize(layoutfont);
192 par->inInset()->getDrawFont(tmpfont);
194 return realizeFont(tmpfont, buf, par);
198 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
200 LyXLayout_ptr const & layout = par->layout();
202 if (!par->getDepth()) {
203 return layout->resfont;
206 return realizeFont(layout->font, buf, par);
210 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
212 LyXLayout_ptr const & layout = par->layout();
214 if (!par->getDepth()) {
215 return layout->reslabelfont;
218 return realizeFont(layout->labelfont, buf, par);
222 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
223 pos_type pos, LyXFont const & fnt,
226 Buffer const * buf = bv->buffer();
227 LyXFont font = getFont(buf, par, pos);
228 font.update(fnt, buf->params.language, toggleall);
229 // Let the insets convert their font
230 if (par->isInset(pos)) {
231 Inset * inset = par->getInset(pos);
232 if (isEditableInset(inset)) {
233 UpdatableInset * uinset =
234 static_cast<UpdatableInset *>(inset);
235 uinset->setFont(bv, fnt, toggleall, true);
239 // Plug thru to version below:
240 setCharFont(buf, par, pos, font);
244 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
245 pos_type pos, LyXFont const & fnt)
249 LyXTextClass const & tclass = buf->params.getLyXTextClass();
250 LyXLayout_ptr const & layout = par->layout();
252 // Get concrete layout font to reduce against
255 if (pos < par->beginningOfBody())
256 layoutfont = layout->labelfont;
258 layoutfont = layout->font;
260 // Realize against environment font information
261 if (par->getDepth()) {
262 Paragraph * tp = par;
263 while (!layoutfont.resolved() && tp && tp->getDepth()) {
264 tp = tp->outerHook();
266 layoutfont.realize(tp->layout()->font);
270 layoutfont.realize(tclass.defaultfont());
272 // Now, reduce font against full layout font
273 font.reduce(layoutfont);
275 par->setFont(pos, font);
279 // inserts a new row before the specified row, increments
280 // the touched counters
281 void LyXText::insertRow(Row * row, Paragraph * par,
284 Row * tmprow = new Row;
287 tmprow->next(firstrow);
290 tmprow->previous(row);
291 tmprow->next(row->next());
296 tmprow->next()->previous(tmprow);
298 if (tmprow->previous())
299 tmprow->previous()->next(tmprow);
310 // removes the row and reset the touched counters
311 void LyXText::removeRow(Row * row) const
313 Row * row_prev = row->previous();
315 row->next()->previous(row_prev);
317 firstrow = row->next();
318 // lyx::Assert(firstrow);
320 row_prev->next(row->next());
322 if (row == lastrow) {
323 lyx::Assert(!row->next());
327 /* FIXME: when we cache the bview, this should just
328 * become a postPaint(), I think */
329 if (refresh_row == row) {
330 refresh_row = row_prev ? row_prev : row->next();
331 // what about refresh_y
334 if (top_row_ == row) {
336 top_row_ = row->next();
337 top_row_offset_ -= row->height();
344 height -= row->height(); // the text becomes smaller
350 // remove all following rows of the paragraph of the specified row.
351 void LyXText::removeParagraph(Row * row) const
353 Paragraph * tmppar = row->par();
357 while (row && row->par() == tmppar) {
358 tmprow = row->next();
365 // insert the specified paragraph behind the specified row
366 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
369 // insert a new row, starting at position 0
370 insertRow(row, par, 0);
372 // and now append the whole paragraph before the new row
375 appendParagraph(bview, firstrow);
377 row->next()->height(0);
378 appendParagraph(bview, row->next());
383 Inset * LyXText::getInset() const
385 if (cursor.pos() < cursor.par()->size()
386 && cursor.par()->isInset(cursor.pos())) {
387 return cursor.par()->getInset(cursor.pos());
393 void LyXText::toggleInset(BufferView * bview)
395 Inset * inset = getInset();
396 // is there an editable inset at cursor position?
397 if (!isEditableInset(inset)) {
398 // No, try to see if we are inside a collapsable inset
399 if (inset_owner && inset_owner->owner()
400 && inset_owner->owner()->isOpen()) {
401 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
402 inset_owner->owner()->close(bview);
403 bview->getLyXText()->cursorRight(bview);
407 //bview->owner()->message(inset->editMessage());
409 // do we want to keep this?? (JMarc)
410 if (!isHighlyEditableInset(inset))
411 setCursorParUndo(bview);
413 if (inset->isOpen()) {
419 inset->open(bview, !inset->isOpen());
424 /* used in setlayout */
425 // Asger is not sure we want to do this...
426 void LyXText::makeFontEntriesLayoutSpecific(Buffer const & buf,
429 LyXLayout_ptr const & layout = par.layout();
432 for (pos_type pos = 0; pos < par.size(); ++pos) {
433 if (pos < par.beginningOfBody())
434 layoutfont = layout->labelfont;
436 layoutfont = layout->font;
438 LyXFont tmpfont = par.getFontSettings(buf.params, pos);
439 tmpfont.reduce(layoutfont);
440 par.setFont(pos, tmpfont);
445 Paragraph * LyXText::setLayout(BufferView * bview,
446 LyXCursor & cur, LyXCursor & sstart_cur,
447 LyXCursor & send_cur,
448 string const & layout)
450 Paragraph * endpar = send_cur.par()->next();
451 Paragraph * undoendpar = endpar;
453 if (endpar && endpar->getDepth()) {
454 while (endpar && endpar->getDepth()) {
455 endpar = endpar->next();
459 endpar = endpar->next(); // because of parindents etc.
462 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
464 // ok we have a selection. This is always between sstart_cur
465 // and sel_end cursor
467 Paragraph * par = sstart_cur.par();
468 Paragraph * epar = send_cur.par()->next();
470 LyXLayout_ptr const & lyxlayout =
471 bview->buffer()->params.getLyXTextClass()[layout];
474 par->applyLayout(lyxlayout);
475 makeFontEntriesLayoutSpecific(*bview->buffer(), *par);
476 Paragraph * fppar = par;
477 fppar->params().spaceTop(lyxlayout->fill_top ?
478 VSpace(VSpace::VFILL)
479 : VSpace(VSpace::NONE));
480 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
481 VSpace(VSpace::VFILL)
482 : VSpace(VSpace::NONE));
483 if (lyxlayout->margintype == MARGIN_MANUAL)
484 par->setLabelWidthString(lyxlayout->labelstring());
487 } while (par != epar);
493 // set layout over selection and make a total rebreak of those paragraphs
494 void LyXText::setLayout(BufferView * bview, string const & layout)
496 LyXCursor tmpcursor = cursor; /* store the current cursor */
498 // if there is no selection just set the layout
499 // of the current paragraph */
500 if (!selection.set()) {
501 selection.start = cursor; // dummy selection
502 selection.end = cursor;
504 Paragraph * endpar = setLayout(bview, cursor, selection.start,
505 selection.end, layout);
506 redoParagraphs(bview, selection.start, endpar);
508 // we have to reset the selection, because the
509 // geometry could have changed
510 setCursor(bview, selection.start.par(),
511 selection.start.pos(), false);
512 selection.cursor = cursor;
513 setCursor(bview, selection.end.par(), selection.end.pos(), false);
514 updateCounters(bview);
517 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
521 // increment depth over selection and
522 // make a total rebreak of those paragraphs
523 void LyXText::incDepth(BufferView * bview)
525 // If there is no selection, just use the current paragraph
526 if (!selection.set()) {
527 selection.start = cursor; // dummy selection
528 selection.end = cursor;
531 // We end at the next paragraph with depth 0
532 Paragraph * endpar = selection.end.par()->next();
534 Paragraph * undoendpar = endpar;
536 if (endpar && endpar->getDepth()) {
537 while (endpar && endpar->getDepth()) {
538 endpar = endpar->next();
542 endpar = endpar->next(); // because of parindents etc.
545 setUndo(bview, Undo::EDIT,
546 selection.start.par(), undoendpar);
548 LyXCursor tmpcursor = cursor; // store the current cursor
550 // ok we have a selection. This is always between sel_start_cursor
551 // and sel_end cursor
552 cursor = selection.start;
555 // NOTE: you can't change the depth of a bibliography entry
556 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
557 Paragraph * prev = cursor.par()->previous();
560 if (cursor.par()->getDepth()
561 < prev->getMaxDepthAfter()) {
562 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
566 if (cursor.par() == selection.end.par())
568 cursor.par(cursor.par()->next());
571 redoParagraphs(bview, selection.start, endpar);
573 // we have to reset the selection, because the
574 // geometry could have changed
575 setCursor(bview, selection.start.par(), selection.start.pos());
576 selection.cursor = cursor;
577 setCursor(bview, selection.end.par(), selection.end.pos());
578 updateCounters(bview);
581 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
585 // decrement depth over selection and
586 // make a total rebreak of those paragraphs
587 void LyXText::decDepth(BufferView * bview)
589 // if there is no selection just set the layout
590 // of the current paragraph
591 if (!selection.set()) {
592 selection.start = cursor; // dummy selection
593 selection.end = cursor;
595 Paragraph * endpar = selection.end.par()->next();
596 Paragraph * undoendpar = endpar;
598 if (endpar && endpar->getDepth()) {
599 while (endpar && endpar->getDepth()) {
600 endpar = endpar->next();
604 endpar = endpar->next(); // because of parindents etc.
607 setUndo(bview, Undo::EDIT,
608 selection.start.par(), undoendpar);
610 LyXCursor tmpcursor = cursor; // store the current cursor
612 // ok we have a selection. This is always between sel_start_cursor
613 // and sel_end cursor
614 cursor = selection.start;
617 if (cursor.par()->params().depth()) {
618 cursor.par()->params()
619 .depth(cursor.par()->params().depth() - 1);
621 if (cursor.par() == selection.end.par()) {
624 cursor.par(cursor.par()->next());
627 redoParagraphs(bview, selection.start, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed
631 setCursor(bview, selection.start.par(),
632 selection.start.pos());
633 selection.cursor = cursor;
634 setCursor(bview, selection.end.par(), selection.end.pos());
635 updateCounters(bview);
638 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
642 // set font over selection and make a total rebreak of those paragraphs
643 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
645 // if there is no selection just set the current_font
646 if (!selection.set()) {
647 // Determine basis font
649 if (cursor.pos() < cursor.par()->beginningOfBody()) {
650 layoutfont = getLabelFont(bview->buffer(),
653 layoutfont = getLayoutFont(bview->buffer(),
656 // Update current font
657 real_current_font.update(font,
658 bview->buffer()->params.language,
661 // Reduce to implicit settings
662 current_font = real_current_font;
663 current_font.reduce(layoutfont);
664 // And resolve it completely
665 real_current_font.realize(layoutfont);
670 LyXCursor tmpcursor = cursor; // store the current cursor
672 // ok we have a selection. This is always between sel_start_cursor
673 // and sel_end cursor
675 setUndo(bview, Undo::EDIT,
676 selection.start.par(), selection.end.par()->next());
678 cursor = selection.start;
679 while (cursor.par() != selection.end.par() ||
680 cursor.pos() < selection.end.pos())
682 if (cursor.pos() < cursor.par()->size()) {
683 // an open footnote should behave like a closed one
684 setCharFont(bview, cursor.par(), cursor.pos(),
686 cursor.pos(cursor.pos() + 1);
689 cursor.par(cursor.par()->next());
694 redoParagraphs(bview, selection.start, selection.end.par()->next());
696 // we have to reset the selection, because the
697 // geometry could have changed, but we keep
698 // it for user convenience
699 setCursor(bview, selection.start.par(), selection.start.pos());
700 selection.cursor = cursor;
701 setCursor(bview, selection.end.par(), selection.end.pos());
703 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
704 tmpcursor.boundary());
708 void LyXText::redoHeightOfParagraph(BufferView * bview)
710 Row * tmprow = cursor.row();
711 int y = cursor.y() - tmprow->baseline();
713 setHeightOfRow(bview, tmprow);
715 while (tmprow->previous()
716 && tmprow->previous()->par() == tmprow->par()) {
717 tmprow = tmprow->previous();
718 y -= tmprow->height();
719 setHeightOfRow(bview, tmprow);
722 postPaint(*bview, y);
724 setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
728 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
730 Row * tmprow = cur.row();
732 int y = cur.y() - tmprow->baseline();
733 setHeightOfRow(bview, tmprow);
735 while (tmprow->previous()
736 && tmprow->previous()->par() == tmprow->par()) {
737 tmprow = tmprow->previous();
738 y -= tmprow->height();
741 postPaint(*bview, y);
742 setCursor(bview, cur.par(), cur.pos());
746 // deletes and inserts again all paragaphs between the cursor
747 // and the specified par
748 // This function is needed after SetLayout and SetFont etc.
749 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
750 Paragraph const * endpar) const
753 Paragraph * tmppar = 0;
754 Paragraph * first_phys_par = 0;
756 Row * tmprow = cur.row();
758 int y = cur.y() - tmprow->baseline();
760 if (!tmprow->previous()) {
761 // a trick/hack for UNDO
762 // This is needed because in an UNDO/REDO we could have changed
763 // the ownerParagrah() so the paragraph inside the row is NOT
764 // my really first par anymore. Got it Lars ;) (Jug 20011206)
765 first_phys_par = ownerParagraph();
767 first_phys_par = tmprow->par();
768 while (tmprow->previous()
769 && tmprow->previous()->par() == first_phys_par)
771 tmprow = tmprow->previous();
772 y -= tmprow->height();
776 Row * prevrow = tmprow->previous();
780 tmppar = tmprow->next()->par();
783 while (tmprow->next() && tmppar != endpar) {
784 removeRow(tmprow->next());
785 if (tmprow->next()) {
786 tmppar = tmprow->next()->par();
792 // remove the first one
793 tmprow2 = tmprow; /* this is because tmprow->previous()
795 tmprow = tmprow->previous();
798 tmppar = first_phys_par;
802 insertParagraph(bview, tmppar, tmprow);
806 while (tmprow->next()
807 && tmprow->next()->par() == tmppar) {
808 tmprow = tmprow->next();
810 tmppar = tmppar->next();
812 } while (tmppar && tmppar != endpar);
814 // this is because of layout changes
816 setHeightOfRow(bview, prevrow);
817 const_cast<LyXText *>(this)->postPaint(*bview, y - prevrow->height());
819 setHeightOfRow(bview, firstrow);
820 const_cast<LyXText *>(this)->postPaint(*bview, 0);
823 if (tmprow && tmprow->next())
824 setHeightOfRow(bview, tmprow->next());
825 updateCounters(bview);
829 void LyXText::fullRebreak(BufferView * bview)
835 if (need_break_row) {
836 breakAgain(bview, need_break_row);
843 // important for the screen
846 // the cursor set functions have a special mechanism. When they
847 // realize, that you left an empty paragraph, they will delete it.
848 // They also delete the corresponding row
850 // need the selection cursor:
851 void LyXText::setSelection(BufferView * bview)
853 bool const lsel = selection.set();
855 if (!selection.set()) {
856 last_sel_cursor = selection.cursor;
857 selection.start = selection.cursor;
858 selection.end = selection.cursor;
863 // first the toggling area
864 if (cursor.y() < last_sel_cursor.y()
865 || (cursor.y() == last_sel_cursor.y()
866 && cursor.x() < last_sel_cursor.x())) {
867 toggle_end_cursor = last_sel_cursor;
868 toggle_cursor = cursor;
870 toggle_end_cursor = cursor;
871 toggle_cursor = last_sel_cursor;
874 last_sel_cursor = cursor;
876 // and now the whole selection
878 if (selection.cursor.par() == cursor.par())
879 if (selection.cursor.pos() < cursor.pos()) {
880 selection.end = cursor;
881 selection.start = selection.cursor;
883 selection.end = selection.cursor;
884 selection.start = cursor;
886 else if (selection.cursor.y() < cursor.y() ||
887 (selection.cursor.y() == cursor.y()
888 && selection.cursor.x() < cursor.x())) {
889 selection.end = cursor;
890 selection.start = selection.cursor;
893 selection.end = selection.cursor;
894 selection.start = cursor;
897 // a selection with no contents is not a selection
898 if (selection.start.par() == selection.end.par() &&
899 selection.start.pos() == selection.end.pos())
900 selection.set(false);
902 if (inset_owner && (selection.set() || lsel))
903 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
907 string const LyXText::selectionAsString(Buffer const * buffer,
910 if (!selection.set()) return string();
912 // should be const ...
913 Paragraph * startpar(selection.start.par());
914 Paragraph * endpar(selection.end.par());
915 pos_type const startpos(selection.start.pos());
916 pos_type const endpos(selection.end.pos());
918 if (startpar == endpar) {
919 return startpar->asString(buffer, startpos, endpos, label);
924 // First paragraph in selection
925 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
927 // The paragraphs in between (if any)
928 LyXCursor tmpcur(selection.start);
929 tmpcur.par(tmpcur.par()->next());
930 while (tmpcur.par() != endpar) {
931 result += tmpcur.par()->asString(buffer, 0,
932 tmpcur.par()->size(),
934 tmpcur.par(tmpcur.par()->next());
937 // Last paragraph in selection
938 result += endpar->asString(buffer, 0, endpos, label);
944 void LyXText::clearSelection() const
946 selection.set(false);
947 selection.mark(false);
948 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
949 // reset this in the bv_owner!
950 if (bv_owner && bv_owner->text)
951 bv_owner->text->xsel_cache.set(false);
955 void LyXText::cursorHome(BufferView * bview) const
957 setCursor(bview, cursor.par(), cursor.row()->pos());
961 void LyXText::cursorEnd(BufferView * bview) const
963 if (!cursor.row()->next()
964 || cursor.row()->next()->par() != cursor.row()->par()) {
965 setCursor(bview, cursor.par(), cursor.row()->lastPos() + 1);
967 if (!cursor.par()->empty() &&
968 (cursor.par()->getChar(cursor.row()->lastPos()) == ' '
969 || cursor.par()->isNewline(cursor.row()->lastPos()))) {
970 setCursor(bview, cursor.par(), cursor.row()->lastPos());
972 setCursor(bview,cursor.par(),
973 cursor.row()->lastPos() + 1);
979 void LyXText::cursorTop(BufferView * bview) const
981 while (cursor.par()->previous())
982 cursor.par(cursor.par()->previous());
983 setCursor(bview, cursor.par(), 0);
987 void LyXText::cursorBottom(BufferView * bview) const
989 while (cursor.par()->next())
990 cursor.par(cursor.par()->next());
991 setCursor(bview, cursor.par(), cursor.par()->size());
995 void LyXText::toggleFree(BufferView * bview,
996 LyXFont const & font, bool toggleall)
998 // If the mask is completely neutral, tell user
999 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1000 // Could only happen with user style
1001 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1005 // Try implicit word selection
1006 // If there is a change in the language the implicit word selection
1008 LyXCursor resetCursor = cursor;
1009 bool implicitSelection = (font.language() == ignore_language
1010 && font.number() == LyXFont::IGNORE)
1011 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1014 setFont(bview, font, toggleall);
1016 // Implicit selections are cleared afterwards
1017 //and cursor is set to the original position.
1018 if (implicitSelection) {
1020 cursor = resetCursor;
1021 setCursor(bview, cursor.par(), cursor.pos());
1022 selection.cursor = cursor;
1025 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1029 string LyXText::getStringToIndex(BufferView * bview)
1031 // Try implicit word selection
1032 // If there is a change in the language the implicit word selection
1034 LyXCursor const reset_cursor = cursor;
1035 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1038 if (!selection.set())
1039 bview->owner()->message(_("Nothing to index!"));
1040 else if (selection.start.par() != selection.end.par())
1041 bview->owner()->message(_("Cannot index more than one paragraph!"));
1043 idxstring = selectionAsString(bview->buffer(), false);
1045 // Reset cursors to their original position.
1046 cursor = reset_cursor;
1047 setCursor(bview, cursor.par(), cursor.pos());
1048 selection.cursor = cursor;
1050 // Clear the implicit selection.
1051 if (implicitSelection)
1058 // the DTP switches for paragraphs. LyX will store them in the first
1059 // physicla paragraph. When a paragraph is broken, the top settings rest,
1060 // the bottom settings are given to the new one. So I can make shure,
1061 // they do not duplicate themself and you cannnot make dirty things with
1064 void LyXText::setParagraph(BufferView * bview,
1065 bool line_top, bool line_bottom,
1066 bool pagebreak_top, bool pagebreak_bottom,
1067 VSpace const & space_top,
1068 VSpace const & space_bottom,
1069 Spacing const & spacing,
1071 string labelwidthstring,
1074 LyXCursor tmpcursor = cursor;
1075 if (!selection.set()) {
1076 selection.start = cursor;
1077 selection.end = cursor;
1080 // make sure that the depth behind the selection are restored, too
1081 Paragraph * endpar = selection.end.par()->next();
1082 Paragraph * undoendpar = endpar;
1084 if (endpar && endpar->getDepth()) {
1085 while (endpar && endpar->getDepth()) {
1086 endpar = endpar->next();
1087 undoendpar = endpar;
1091 // because of parindents etc.
1092 endpar = endpar->next();
1095 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1098 Paragraph * tmppar = selection.end.par();
1100 while (tmppar != selection.start.par()->previous()) {
1101 setCursor(bview, tmppar, 0);
1102 postPaint(*bview, cursor.y() - cursor.row()->baseline());
1103 cursor.par()->params().lineTop(line_top);
1104 cursor.par()->params().lineBottom(line_bottom);
1105 cursor.par()->params().pagebreakTop(pagebreak_top);
1106 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1107 cursor.par()->params().spaceTop(space_top);
1108 cursor.par()->params().spaceBottom(space_bottom);
1109 cursor.par()->params().spacing(spacing);
1110 // does the layout allow the new alignment?
1111 LyXLayout_ptr const & layout = cursor.par()->layout();
1113 if (align == LYX_ALIGN_LAYOUT)
1114 align = layout->align;
1115 if (align & layout->alignpossible) {
1116 if (align == layout->align)
1117 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1119 cursor.par()->params().align(align);
1121 cursor.par()->setLabelWidthString(labelwidthstring);
1122 cursor.par()->params().noindent(noindent);
1123 tmppar = cursor.par()->previous();
1126 redoParagraphs(bview, selection.start, endpar);
1129 setCursor(bview, selection.start.par(), selection.start.pos());
1130 selection.cursor = cursor;
1131 setCursor(bview, selection.end.par(), selection.end.pos());
1132 setSelection(bview);
1133 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1135 bview->updateInset(inset_owner, true);
1139 // set the counter of a paragraph. This includes the labels
1140 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1142 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1143 LyXLayout_ptr const & layout = par->layout();
1145 if (par->previous()) {
1147 par->params().appendix(par->previous()->params().appendix());
1148 if (!par->params().appendix() && par->params().startOfAppendix()) {
1149 par->params().appendix(true);
1150 textclass.counters().reset();
1152 par->enumdepth = par->previous()->enumdepth;
1153 par->itemdepth = par->previous()->itemdepth;
1155 par->params().appendix(par->params().startOfAppendix());
1160 /* Maybe we have to increment the enumeration depth.
1161 * BUT, enumeration in a footnote is considered in isolation from its
1162 * surrounding paragraph so don't increment if this is the
1163 * first line of the footnote
1164 * AND, bibliographies can't have their depth changed ie. they
1165 * are always of depth 0
1168 && par->previous()->getDepth() < par->getDepth()
1169 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1170 && par->enumdepth < 3
1171 && layout->labeltype != LABEL_BIBLIO) {
1175 // Maybe we have to decrement the enumeration depth, see note above
1177 && par->previous()->getDepth() > par->getDepth()
1178 && layout->labeltype != LABEL_BIBLIO) {
1179 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1182 if (!par->params().labelString().empty()) {
1183 par->params().labelString(string());
1186 if (layout->margintype == MARGIN_MANUAL) {
1187 if (par->params().labelWidthString().empty()) {
1188 par->setLabelWidthString(layout->labelstring());
1191 par->setLabelWidthString(string());
1194 // is it a layout that has an automatic label?
1195 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1196 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1200 if (i >= 0 && i <= buf->params.secnumdepth) {
1204 textclass.counters().step(layout->latexname());
1206 // Is there a label? Useful for Chapter layout
1207 if (!par->params().appendix()) {
1208 s << layout->labelstring();
1210 s << layout->labelstring_appendix();
1213 // Use of an integer is here less than elegant. For now.
1214 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1215 if (!par->params().appendix()) {
1216 numbertype = "sectioning";
1218 numbertype = "appendix";
1219 if (par->isRightToLeftPar(buf->params))
1220 langtype = "hebrew";
1225 s << textclass.counters()
1226 .numberLabel(layout->latexname(),
1227 numbertype, langtype, head);
1229 par->params().labelString(STRCONV(s.str()));
1231 // reset enum counters
1232 textclass.counters().reset("enum");
1233 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1234 textclass.counters().reset("enum");
1235 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1237 // Yes I know this is a really, really! bad solution
1239 string enumcounter("enum");
1241 switch (par->enumdepth) {
1250 enumcounter += "iv";
1253 // not a valid enumdepth...
1257 textclass.counters().step(enumcounter);
1259 s << textclass.counters()
1260 .numberLabel(enumcounter, "enumeration");
1261 par->params().labelString(STRCONV(s.str()));
1263 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1264 textclass.counters().step("bibitem");
1265 int number = textclass.counters().value("bibitem");
1266 if (par->bibitem()) {
1267 par->bibitem()->setCounter(number);
1268 par->params().labelString(layout->labelstring());
1270 // In biblio should't be following counters but...
1272 string s = layout->labelstring();
1274 // the caption hack:
1275 if (layout->labeltype == LABEL_SENSITIVE) {
1276 Paragraph * tmppar = par;
1279 while (tmppar && tmppar->inInset()
1280 // the single '=' is intended below
1281 && (in = tmppar->inInset()->owner())) {
1282 if (in->lyxCode() == Inset::FLOAT_CODE ||
1283 in->lyxCode() == Inset::WRAP_CODE) {
1287 tmppar = in->parOwner();
1293 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1295 textclass.counters().step(fl.type());
1297 // Doesn't work... yet.
1298 #warning use boost.format
1299 #if USE_BOOST_FORMAT
1300 s = boost::io::str(boost::format(_("%1$s #:")) % fl.name());
1301 // s << boost::format(_("%1$s %1$d:")
1303 // % buf->counters().value(fl.name());
1306 //o << fl.name() << ' ' << buf->counters().value(fl.name()) << ":";
1307 o << fl.name() << " #:";
1308 s = STRCONV(o.str());
1311 // par->SetLayout(0);
1312 // s = layout->labelstring;
1313 s = _("Senseless: ");
1316 par->params().labelString(s);
1318 // reset the enumeration counter. They are always reset
1319 // when there is any other layout between
1320 // Just fall-through between the cases so that all
1321 // enum counters deeper than enumdepth is also reset.
1322 switch (par->enumdepth) {
1324 textclass.counters().reset("enumi");
1326 textclass.counters().reset("enumii");
1328 textclass.counters().reset("enumiii");
1330 textclass.counters().reset("enumiv");
1336 // Updates all counters. Paragraphs with changed label string will be rebroken
1337 void LyXText::updateCounters(BufferView * bview) const
1339 Row * row = firstrow;
1340 Paragraph * par = row->par();
1342 // CHECK if this is really needed. (Lgb)
1343 bview->buffer()->params.getLyXTextClass().counters().reset();
1346 while (row->par() != par)
1349 string const oldLabel = par->params().labelString();
1351 // setCounter can potentially change the labelString.
1352 setCounter(bview->buffer(), par);
1354 string const & newLabel = par->params().labelString();
1356 if (oldLabel.empty() && !newLabel.empty()) {
1357 removeParagraph(row);
1358 appendParagraph(bview, row);
1366 void LyXText::insertInset(BufferView * bview, Inset * inset)
1368 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1370 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1372 cursor.par()->insertInset(cursor.pos(), inset);
1373 // Just to rebreak and refresh correctly.
1374 // The character will not be inserted a second time
1375 insertChar(bview, Paragraph::META_INSET);
1376 // If we enter a highly editable inset the cursor should be to before
1377 // the inset. This couldn't happen before as Undo was not handled inside
1378 // inset now after the Undo LyX tries to call inset->Edit(...) again
1379 // and cannot do this as the cursor is behind the inset and GetInset
1380 // does not return the inset!
1381 if (isHighlyEditableInset(inset)) {
1382 cursorLeft(bview, true);
1388 void LyXText::copyEnvironmentType()
1390 copylayouttype = cursor.par()->layout()->name();
1394 void LyXText::pasteEnvironmentType(BufferView * bview)
1396 // do nothing if there has been no previous copyEnvironmentType()
1397 if (!copylayouttype.empty())
1398 setLayout(bview, copylayouttype);
1402 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1404 // Stuff what we got on the clipboard. Even if there is no selection.
1406 // There is a problem with having the stuffing here in that the
1407 // larger the selection the slower LyX will get. This can be
1408 // solved by running the line below only when the selection has
1409 // finished. The solution used currently just works, to make it
1410 // faster we need to be more clever and probably also have more
1411 // calls to stuffClipboard. (Lgb)
1412 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1414 // This doesn't make sense, if there is no selection
1415 if (!selection.set())
1418 // OK, we have a selection. This is always between selection.start
1419 // and selection.end
1421 // make sure that the depth behind the selection are restored, too
1422 Paragraph * endpar = selection.end.par()->next();
1423 Paragraph * undoendpar = endpar;
1425 if (endpar && endpar->getDepth()) {
1426 while (endpar && endpar->getDepth()) {
1427 endpar = endpar->next();
1428 undoendpar = endpar;
1430 } else if (endpar) {
1431 endpar = endpar->next(); // because of parindents etc.
1434 setUndo(bview, Undo::DELETE,
1435 selection.start.par(), undoendpar);
1437 // there are two cases: cut only within one paragraph or
1438 // more than one paragraph
1439 if (selection.start.par() == selection.end.par()) {
1440 // only within one paragraph
1441 endpar = selection.end.par();
1442 int pos = selection.end.pos();
1443 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1444 selection.start.pos(), pos,
1445 bview->buffer()->params.textclass,
1447 selection.end.pos(pos);
1449 endpar = selection.end.par();
1450 int pos = selection.end.pos();
1451 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1452 selection.start.pos(), pos,
1453 bview->buffer()->params.textclass,
1456 selection.end.par(endpar);
1457 selection.end.pos(pos);
1458 cursor.pos(selection.end.pos());
1460 endpar = endpar->next();
1462 // sometimes necessary
1464 selection.start.par()->stripLeadingSpaces();
1466 redoParagraphs(bview, selection.start, endpar);
1468 // cutSelection can invalidate the cursor so we need to set
1470 // we prefer the end for when tracking changes
1471 cursor = selection.end;
1473 // need a valid cursor. (Lgb)
1476 setCursor(bview, cursor.par(), cursor.pos());
1477 selection.cursor = cursor;
1478 updateCounters(bview);
1482 void LyXText::copySelection(BufferView * bview)
1484 // stuff the selection onto the X clipboard, from an explicit copy request
1485 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1487 // this doesnt make sense, if there is no selection
1488 if (!selection.set())
1491 // ok we have a selection. This is always between selection.start
1492 // and sel_end cursor
1494 // copy behind a space if there is one
1495 while (selection.start.par()->size() > selection.start.pos()
1496 && selection.start.par()->isLineSeparator(selection.start.pos())
1497 && (selection.start.par() != selection.end.par()
1498 || selection.start.pos() < selection.end.pos()))
1499 selection.start.pos(selection.start.pos() + 1);
1501 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1502 selection.start.pos(), selection.end.pos(),
1503 bview->buffer()->params.textclass);
1507 void LyXText::pasteSelection(BufferView * bview)
1509 // this does not make sense, if there is nothing to paste
1510 if (!CutAndPaste::checkPastePossible())
1513 setUndo(bview, Undo::INSERT,
1514 cursor.par(), cursor.par()->next());
1517 Paragraph * actpar = cursor.par();
1518 int pos = cursor.pos();
1520 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1521 bview->buffer()->params.textclass);
1523 redoParagraphs(bview, cursor, endpar);
1525 setCursor(bview, cursor.par(), cursor.pos());
1528 selection.cursor = cursor;
1529 setCursor(bview, actpar, pos);
1530 setSelection(bview);
1531 updateCounters(bview);
1535 void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
1540 selection.cursor = cursor;
1543 setSelection(bview);
1547 // simple replacing. The font of the first selected character is used
1548 void LyXText::replaceSelectionWithString(BufferView * bview,
1551 setCursorParUndo(bview);
1554 if (!selection.set()) { // create a dummy selection
1555 selection.end = cursor;
1556 selection.start = cursor;
1559 // Get font setting before we cut
1560 pos_type pos = selection.end.pos();
1561 LyXFont const font = selection.start.par()
1562 ->getFontSettings(bview->buffer()->params,
1563 selection.start.pos());
1565 // Insert the new string
1566 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1567 selection.end.par()->insertChar(pos, (*cit), font);
1571 // Cut the selection
1572 cutSelection(bview, true, false);
1578 // needed to insert the selection
1579 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1581 Paragraph * par = cursor.par();
1582 pos_type pos = cursor.pos();
1583 Paragraph * endpar = cursor.par()->next();
1585 setCursorParUndo(bview);
1587 // only to be sure, should not be neccessary
1590 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1592 redoParagraphs(bview, cursor, endpar);
1593 setCursor(bview, cursor.par(), cursor.pos());
1594 selection.cursor = cursor;
1595 setCursor(bview, par, pos);
1596 setSelection(bview);
1600 // turns double-CR to single CR, others where converted into one
1601 // blank. Then InsertStringAsLines is called
1602 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1604 string linestr(str);
1605 bool newline_inserted = false;
1606 for (string::size_type i = 0; i < linestr.length(); ++i) {
1607 if (linestr[i] == '\n') {
1608 if (newline_inserted) {
1609 // we know that \r will be ignored by
1610 // InsertStringA. Of course, it is a dirty
1611 // trick, but it works...
1612 linestr[i - 1] = '\r';
1616 newline_inserted = true;
1618 } else if (IsPrintable(linestr[i])) {
1619 newline_inserted = false;
1622 insertStringAsLines(bview, linestr);
1626 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1629 LyXCursor tmpcursor;
1633 Row * row = getRow(par, pos, y);
1635 // is there a break one row above
1636 if (row->previous() && row->previous()->par() == row->par()) {
1637 z = rowBreakPoint(*bview, *row->previous());
1638 if (z >= row->pos()) {
1639 // set the dimensions of the row above
1640 y -= row->previous()->height();
1641 postPaint(*bview, y);
1643 breakAgain(bview, row->previous());
1645 // set the cursor again. Otherwise
1646 // dangling pointers are possible
1647 setCursor(bview, cursor.par(), cursor.pos(),
1648 false, cursor.boundary());
1649 selection.cursor = cursor;
1654 int const tmpheight = row->height();
1655 pos_type const tmplast = row->lastPos();
1657 breakAgain(bview, row);
1658 if (row->height() == tmpheight && row->lastPos() == tmplast) {
1659 postRowPaint(*bview, row, y);
1661 postPaint(*bview, y);
1664 // check the special right address boxes
1665 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1672 redoDrawingOfParagraph(bview, tmpcursor);
1675 // set the cursor again. Otherwise dangling pointers are possible
1676 // also set the selection
1678 if (selection.set()) {
1680 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1681 false, selection.cursor.boundary());
1682 selection.cursor = cursor;
1683 setCursorIntern(bview, selection.start.par(),
1684 selection.start.pos(),
1685 false, selection.start.boundary());
1686 selection.start = cursor;
1687 setCursorIntern(bview, selection.end.par(),
1688 selection.end.pos(),
1689 false, selection.end.boundary());
1690 selection.end = cursor;
1691 setCursorIntern(bview, last_sel_cursor.par(),
1692 last_sel_cursor.pos(),
1693 false, last_sel_cursor.boundary());
1694 last_sel_cursor = cursor;
1697 setCursorIntern(bview, cursor.par(), cursor.pos(),
1698 false, cursor.boundary());
1702 // returns false if inset wasn't found
1703 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1705 // first check the current paragraph
1706 int pos = cursor.par()->getPositionOfInset(inset);
1708 checkParagraph(bview, cursor.par(), pos);
1712 // check every paragraph
1714 Paragraph * par = ownerParagraph();
1716 pos = par->getPositionOfInset(inset);
1718 checkParagraph(bview, par, pos);
1728 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1730 bool setfont, bool boundary) const
1732 LyXCursor old_cursor = cursor;
1733 setCursorIntern(bview, par, pos, setfont, boundary);
1734 return deleteEmptyParagraphMechanism(bview, old_cursor);
1738 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1739 pos_type pos, bool boundary) const
1746 cur.boundary(boundary);
1748 // get the cursor y position in text
1750 Row * row = getRow(par, pos, y);
1751 Row * old_row = row;
1753 // if we are before the first char of this row and are still in the
1754 // same paragraph and there is a previous row then put the cursor on
1755 // the end of the previous row
1756 cur.iy(y + row->baseline());
1758 if (row->previous() && pos &&
1759 row->previous()->par() == row->par() &&
1760 pos < par->size() &&
1761 par->getChar(pos) == Paragraph::META_INSET &&
1762 (ins = par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1764 row = row->previous();
1769 // y is now the beginning of the cursor row
1770 y += row->baseline();
1771 // y is now the cursor baseline
1774 pos_type last = old_row->lastPrintablePos();
1776 // None of these should happen, but we're scaredy-cats
1777 if (pos > par->size()) {
1778 lyxerr << "dont like 1 please report" << endl;
1781 } else if (pos > last + 1) {
1782 lyxerr << "dont like 2 please report" << endl;
1783 // This shouldn't happen.
1786 } else if (pos < row->pos()) {
1787 lyxerr << "dont like 3 please report" << endl;
1792 // now get the cursors x position
1793 float x = getCursorX(bview, row, pos, last, boundary);
1796 if (old_row != row) {
1797 x = getCursorX(bview, old_row, pos, last, boundary);
1804 float LyXText::getCursorX(BufferView * bview, Row * row,
1805 pos_type pos, pos_type last, bool boundary) const
1807 pos_type cursor_vpos = 0;
1809 float fill_separator;
1811 float fill_label_hfill;
1812 // This call HAS to be here because of the BidiTables!!!
1813 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1816 if (last < row->pos())
1817 cursor_vpos = row->pos();
1818 else if (pos > last && !boundary)
1819 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1820 ? row->pos() : last + 1;
1821 else if (pos > row->pos() &&
1822 (pos > last || boundary))
1823 /// Place cursor after char at (logical) position pos - 1
1824 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1825 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1827 /// Place cursor before char at (logical) position pos
1828 cursor_vpos = (bidi_level(pos) % 2 == 0)
1829 ? log2vis(pos) : log2vis(pos) + 1;
1831 pos_type body_pos = row->par()->beginningOfBody();
1832 if ((body_pos > 0) &&
1833 ((body_pos-1 > last) ||
1834 !row->par()->isLineSeparator(body_pos-1)))
1837 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1838 pos_type pos = vis2log(vpos);
1839 if (body_pos > 0 && pos == body_pos - 1) {
1840 x += fill_label_hfill +
1841 font_metrics::width(
1842 row->par()->layout()->labelsep,
1843 getLabelFont(bview->buffer(),
1845 if (row->par()->isLineSeparator(body_pos - 1))
1846 x -= singleWidth(bview,
1847 row->par(), body_pos - 1);
1849 if (row->hfillExpansion(pos)) {
1850 x += singleWidth(bview, row->par(), pos);
1851 if (pos >= body_pos)
1854 x += fill_label_hfill;
1855 } else if (row->par()->isSeparator(pos)) {
1856 x += singleWidth(bview, row->par(), pos);
1857 if (pos >= body_pos)
1858 x += fill_separator;
1860 x += singleWidth(bview, row->par(), pos);
1866 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1867 pos_type pos, bool setfont, bool boundary) const
1869 InsetText * it = static_cast<InsetText *>(par->inInset());
1871 if (it != inset_owner) {
1872 lyxerr[Debug::INSETS] << "InsetText is " << it
1874 << "inset_owner is "
1875 << inset_owner << endl;
1876 #ifdef WITH_WARNINGS
1877 #warning I believe this code is wrong. (Lgb)
1878 #warning Jürgen, have a look at this. (Lgb)
1879 #warning Hmmm, I guess you are right but we
1880 #warning should verify when this is needed
1882 // Jürgen, would you like to have a look?
1883 // I guess we need to move the outer cursor
1884 // and open and lock the inset (bla bla bla)
1885 // stuff I don't know... so can you have a look?
1887 // I moved the lyxerr stuff in here so we can see if
1888 // this is actually really needed and where!
1890 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1895 setCursor(bview, cursor, par, pos, boundary);
1897 setCurrentFont(bview);
1901 void LyXText::setCurrentFont(BufferView * bview) const
1903 pos_type pos = cursor.pos();
1904 if (cursor.boundary() && pos > 0)
1908 if (pos == cursor.par()->size())
1910 else // potentional bug... BUG (Lgb)
1911 if (cursor.par()->isSeparator(pos)) {
1912 if (pos > cursor.row()->pos() &&
1913 bidi_level(pos) % 2 ==
1914 bidi_level(pos - 1) % 2)
1916 else if (pos + 1 < cursor.par()->size())
1922 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1923 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1925 if (cursor.pos() == cursor.par()->size() &&
1926 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1927 !cursor.boundary()) {
1928 Language const * lang =
1929 cursor.par()->getParLanguage(bview->buffer()->params);
1930 current_font.setLanguage(lang);
1931 current_font.setNumber(LyXFont::OFF);
1932 real_current_font.setLanguage(lang);
1933 real_current_font.setNumber(LyXFont::OFF);
1938 // returns the column near the specified x-coordinate of the row
1939 // x is set to the real beginning of this column
1941 LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
1942 bool & boundary) const
1945 float fill_separator;
1947 float fill_label_hfill;
1949 prepareToPrint(bview, row, tmpx, fill_separator,
1950 fill_hfill, fill_label_hfill);
1952 pos_type vc = row->pos();
1953 pos_type last = row->lastPrintablePos();
1956 LyXLayout_ptr const & layout = row->par()->layout();
1958 bool left_side = false;
1960 pos_type body_pos = row->par()->beginningOfBody();
1961 float last_tmpx = tmpx;
1964 (body_pos - 1 > last ||
1965 !row->par()->isLineSeparator(body_pos - 1)))
1968 // check for empty row
1969 if (!row->par()->size()) {
1974 while (vc <= last && tmpx <= x) {
1977 if (body_pos > 0 && c == body_pos-1) {
1978 tmpx += fill_label_hfill +
1979 font_metrics::width(layout->labelsep,
1980 getLabelFont(bview->buffer(), row->par()));
1981 if (row->par()->isLineSeparator(body_pos - 1))
1982 tmpx -= singleWidth(bview, row->par(), body_pos-1);
1985 if (row->hfillExpansion(c)) {
1986 tmpx += singleWidth(bview, row->par(), c);
1990 tmpx += fill_label_hfill;
1991 } else if (row->par()->isSeparator(c)) {
1992 tmpx += singleWidth(bview, row->par(), c);
1994 tmpx+= fill_separator;
1996 tmpx += singleWidth(bview, row->par(), c);
2001 if ((tmpx + last_tmpx) / 2 > x) {
2006 if (vc > last + 1) // This shouldn't happen.
2010 bool const lastrow = lyxrc.rtl_support // This is not needed, but gives
2011 // some speedup if rtl_support=false
2012 && (!row->next() || row->next()->par() != row->par());
2013 bool const rtl = (lastrow)
2014 ? row->par()->isRightToLeftPar(bview->buffer()->params)
2015 : false; // If lastrow is false, we don't need to compute
2016 // the value of rtl.
2019 ((rtl && left_side && vc == row->pos() && x < tmpx - 5) ||
2020 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
2022 else if (vc == row->pos()) {
2024 if (bidi_level(c) % 2 == 1)
2027 c = vis2log(vc - 1);
2028 bool const rtl = (bidi_level(c) % 2 == 1);
2029 if (left_side == rtl) {
2031 boundary = isBoundary(bview->buffer(), row->par(), c);
2035 if (row->pos() <= last && c > last
2036 && row->par()->isNewline(last)) {
2037 if (bidi_level(last) % 2 == 0)
2038 tmpx -= singleWidth(bview, row->par(), last);
2040 tmpx += singleWidth(bview, row->par(), last);
2050 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2052 LyXCursor old_cursor = cursor;
2054 setCursorFromCoordinates(bview, cursor, x, y);
2055 setCurrentFont(bview);
2056 deleteEmptyParagraphMechanism(bview, old_cursor);
2063 * return true if the cursor given is at the end of a row,
2064 * and the next row is filled by an inset that spans an entire
2067 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2070 Row const & next = *row.next();
2072 if (next.pos() != cur.pos() || next.par() != cur.par())
2074 if (!cur.par()->isInset(cur.pos()))
2076 Inset const * inset = cur.par()->getInset(cur.pos());
2077 if (inset->needFullRow() || inset->display())
2084 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2087 // Get the row first.
2089 Row * row = getRowNearY(y);
2091 pos_type const column = getColumnNearX(bview, row, x, bound);
2092 cur.par(row->par());
2093 cur.pos(row->pos() + column);
2095 cur.y(y + row->baseline());
2098 if (beforeFullRowInset(*row, cur)) {
2099 pos_type last = row->lastPrintablePos();
2100 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2102 cur.iy(y + row->height() + row->next()->baseline());
2103 cur.irow(row->next());
2109 cur.boundary(bound);
2113 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2115 if (cursor.pos() > 0) {
2116 bool boundary = cursor.boundary();
2117 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2118 if (!internal && !boundary &&
2119 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2120 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2121 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2122 Paragraph * par = cursor.par()->previous();
2123 setCursor(bview, par, par->size());
2128 void LyXText::cursorRight(BufferView * bview, bool internal) const
2130 if (!internal && cursor.boundary() &&
2131 !cursor.par()->isNewline(cursor.pos()))
2132 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2133 else if (cursor.pos() < cursor.par()->size()) {
2134 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2136 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2137 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2138 } else if (cursor.par()->next())
2139 setCursor(bview, cursor.par()->next(), 0);
2143 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2146 int x = cursor.x_fix();
2147 int y = cursor.y() - cursor.row()->baseline() - 1;
2148 setCursorFromCoordinates(bview, x, y);
2151 int y1 = cursor.iy() - topy;
2154 Inset * inset_hit = checkInsetHit(bview, x, y1);
2155 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2156 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2160 setCursorFromCoordinates(bview, cursor.x_fix(),
2161 cursor.y() - cursor.row()->baseline() - 1);
2166 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2169 int x = cursor.x_fix();
2170 int y = cursor.y() - cursor.row()->baseline() +
2171 cursor.row()->height() + 1;
2172 setCursorFromCoordinates(bview, x, y);
2173 if (!selecting && cursor.row() == cursor.irow()) {
2175 int y1 = cursor.iy() - topy;
2178 Inset * inset_hit = checkInsetHit(bview, x, y1);
2179 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2180 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2184 setCursorFromCoordinates(bview, cursor.x_fix(),
2185 cursor.y() - cursor.row()->baseline()
2186 + cursor.row()->height() + 1);
2191 void LyXText::cursorUpParagraph(BufferView * bview) const
2193 if (cursor.pos() > 0) {
2194 setCursor(bview, cursor.par(), 0);
2196 else if (cursor.par()->previous()) {
2197 setCursor(bview, cursor.par()->previous(), 0);
2202 void LyXText::cursorDownParagraph(BufferView * bview) const
2204 if (cursor.par()->next()) {
2205 setCursor(bview, cursor.par()->next(), 0);
2207 setCursor(bview, cursor.par(), cursor.par()->size());
2211 // fix the cursor `cur' after a characters has been deleted at `where'
2212 // position. Called by deleteEmptyParagraphMechanism
2213 void LyXText::fixCursorAfterDelete(BufferView * bview,
2215 LyXCursor const & where) const
2217 // if cursor is not in the paragraph where the delete occured,
2219 if (cur.par() != where.par())
2222 // if cursor position is after the place where the delete occured,
2224 if (cur.pos() > where.pos())
2225 cur.pos(cur.pos()-1);
2227 // check also if we don't want to set the cursor on a spot behind the
2228 // pagragraph because we erased the last character.
2229 if (cur.pos() > cur.par()->size())
2230 cur.pos(cur.par()->size());
2232 // recompute row et al. for this cursor
2233 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2237 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2238 LyXCursor const & old_cursor) const
2240 // Would be wrong to delete anything if we have a selection.
2241 if (selection.set())
2244 // We allow all kinds of "mumbo-jumbo" when freespacing.
2245 if (old_cursor.par()->layout()->free_spacing
2246 || old_cursor.par()->isFreeSpacing()) {
2250 /* Ok I'll put some comments here about what is missing.
2251 I have fixed BackSpace (and thus Delete) to not delete
2252 double-spaces automagically. I have also changed Cut,
2253 Copy and Paste to hopefully do some sensible things.
2254 There are still some small problems that can lead to
2255 double spaces stored in the document file or space at
2256 the beginning of paragraphs. This happens if you have
2257 the cursor betwenn to spaces and then save. Or if you
2258 cut and paste and the selection have a space at the
2259 beginning and then save right after the paste. I am
2260 sure none of these are very hard to fix, but I will
2261 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2262 that I can get some feedback. (Lgb)
2265 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2266 // delete the LineSeparator.
2269 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2270 // delete the LineSeparator.
2273 // If the pos around the old_cursor were spaces, delete one of them.
2274 if (old_cursor.par() != cursor.par()
2275 || old_cursor.pos() != cursor.pos()) {
2276 // Only if the cursor has really moved
2278 if (old_cursor.pos() > 0
2279 && old_cursor.pos() < old_cursor.par()->size()
2280 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2281 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2282 old_cursor.par()->erase(old_cursor.pos() - 1);
2283 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2285 #ifdef WITH_WARNINGS
2286 #warning This will not work anymore when we have multiple views of the same buffer
2287 // In this case, we will have to correct also the cursors held by
2288 // other bufferviews. It will probably be easier to do that in a more
2289 // automated way in LyXCursor code. (JMarc 26/09/2001)
2291 // correct all cursors held by the LyXText
2292 fixCursorAfterDelete(bview, cursor, old_cursor);
2293 fixCursorAfterDelete(bview, selection.cursor,
2295 fixCursorAfterDelete(bview, selection.start,
2297 fixCursorAfterDelete(bview, selection.end, old_cursor);
2298 fixCursorAfterDelete(bview, last_sel_cursor,
2300 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2301 fixCursorAfterDelete(bview, toggle_end_cursor,
2307 // don't delete anything if this is the ONLY paragraph!
2308 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2311 // Do not delete empty paragraphs with keepempty set.
2312 if (old_cursor.par()->layout()->keepempty)
2315 // only do our magic if we changed paragraph
2316 if (old_cursor.par() == cursor.par())
2319 // record if we have deleted a paragraph
2320 // we can't possibly have deleted a paragraph before this point
2321 bool deleted = false;
2323 if ((old_cursor.par()->empty()
2324 || (old_cursor.par()->size() == 1
2325 && old_cursor.par()->isLineSeparator(0)))) {
2326 // ok, we will delete anything
2327 LyXCursor tmpcursor;
2331 if (old_cursor.row()->previous()) {
2332 const_cast<LyXText *>(this)->postPaint(*bview, old_cursor.y() - old_cursor.row()->baseline()
2333 - old_cursor.row()->previous()->height());
2335 cursor = old_cursor; // that undo can restore the right cursor position
2336 Paragraph * endpar = old_cursor.par()->next();
2337 if (endpar && endpar->getDepth()) {
2338 while (endpar && endpar->getDepth()) {
2339 endpar = endpar->next();
2342 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2346 removeRow(old_cursor.row());
2347 if (ownerParagraph() == old_cursor.par()) {
2348 ownerParagraph(ownerParagraph()->next());
2351 delete old_cursor.par();
2353 /* Breakagain the next par. Needed because of
2354 * the parindent that can occur or dissappear.
2355 * The next row can change its height, if
2356 * there is another layout before */
2357 if (refresh_row->next()) {
2358 breakAgain(bview, refresh_row->next());
2359 updateCounters(bview);
2361 setHeightOfRow(bview, refresh_row);
2363 Row * nextrow = old_cursor.row()->next();
2364 const_cast<LyXText *>(this)->postPaint(*bview,
2365 old_cursor.y() - old_cursor.row()->baseline());
2368 cursor = old_cursor; // that undo can restore the right cursor position
2369 Paragraph * endpar = old_cursor.par()->next();
2370 if (endpar && endpar->getDepth()) {
2371 while (endpar && endpar->getDepth()) {
2372 endpar = endpar->next();
2375 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2379 removeRow(old_cursor.row());
2381 if (ownerParagraph() == old_cursor.par()) {
2382 ownerParagraph(ownerParagraph()->next());
2385 delete old_cursor.par();
2387 /* Breakagain the next par. Needed because of
2388 the parindent that can occur or dissappear.
2389 The next row can change its height, if
2390 there is another layout before */
2392 breakAgain(bview, nextrow);
2393 updateCounters(bview);
2398 setCursorIntern(bview, cursor.par(), cursor.pos());
2400 if (selection.cursor.par() == old_cursor.par()
2401 && selection.cursor.pos() == old_cursor.pos()) {
2402 // correct selection
2403 selection.cursor = cursor;
2407 if (old_cursor.par()->stripLeadingSpaces()) {
2408 redoParagraphs(bview, old_cursor,
2409 old_cursor.par()->next());
2411 setCursorIntern(bview, cursor.par(), cursor.pos());
2412 selection.cursor = cursor;
2419 Paragraph * LyXText::ownerParagraph() const
2422 return inset_owner->paragraph();
2424 return &*(bv_owner->buffer()->paragraphs.begin());
2428 void LyXText::ownerParagraph(Paragraph * p) const
2431 inset_owner->paragraph(p);
2433 bv_owner->buffer()->paragraphs.set(p);
2438 void LyXText::ownerParagraph(int id, Paragraph * p) const
2440 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2441 if (op && op->inInset()) {
2442 static_cast<InsetText *>(op->inInset())->paragraph(p);
2449 LyXText::text_status LyXText::status() const
2455 void LyXText::clearPaint()
2457 status_ = UNCHANGED;
2463 void LyXText::postChangedInDraw()
2465 status_ = CHANGED_IN_DRAW;
2469 void LyXText::postPaint(BufferView & bv, int start_y)
2471 text_status old = status_;
2473 status_ = NEED_MORE_REFRESH;
2476 if (old != UNCHANGED && refresh_y < start_y) {
2477 lyxerr << "Paint already pending from above" << endl;
2481 refresh_y = start_y;
2486 // We are an inset's lyxtext. Tell the top-level lyxtext
2487 // it needs to update the row we're in.
2489 LyXText * t = bv.text;
2491 // FIXME: but what if this row is below ?
2492 if (!t->refresh_row) {
2493 t->refresh_row = t->cursor.row();
2494 t->refresh_y = t->cursor.y() - t->cursor.row()->baseline();
2499 // FIXME: we should probably remove this y parameter,
2500 // make refresh_y be 0, and use row->y etc.
2501 void LyXText::postRowPaint(BufferView & bv, Row * row, int start_y)
2503 if (status_ != UNCHANGED && refresh_y < start_y) {
2504 lyxerr << "Paint already pending from above" << endl;
2507 refresh_y = start_y;
2510 if (status_ == NEED_MORE_REFRESH)
2513 status_ = NEED_VERY_LITTLE_REFRESH;
2519 // We are an inset's lyxtext. Tell the top-level lyxtext
2520 // it needs to update the row we're in.
2522 LyXText * t = bv.text;
2524 // FIXME: but what if this new row is above ?
2525 // Why the !t->refresh_row at all ?
2526 if (!t->refresh_row) {
2527 t->refresh_row = t->cursor.row();
2528 t->refresh_y = t->cursor.y() - t->cursor.row()->baseline();
2533 bool LyXText::isTopLevel() const
2535 /// only the top-level lyxtext has a non-null bv owner
2540 bool LyXText::isInInset() const
2546 int defaultRowHeight()
2548 LyXFont const font(LyXFont::ALL_SANE);
2549 return int(font_metrics::maxAscent(font)
2550 + font_metrics::maxDescent(font) * 1.5);