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 : 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 : 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 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;
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 < beginningOfMainBody(buf, par)) {
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 < beginningOfMainBody(buf, par)) {
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 < beginningOfMainBody(buf, par))
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 behind 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());
326 if (refresh_row == row) {
327 refresh_row = row_prev ? row_prev : row->next();
328 // what about refresh_y, refresh_height
331 height -= row->height(); // the text becomes smaller
337 // remove all following rows of the paragraph of the specified row.
338 void LyXText::removeParagraph(Row * row) const
340 Paragraph * tmppar = row->par();
344 while (row && row->par() == tmppar) {
345 tmprow = row->next();
352 // insert the specified paragraph behind the specified row
353 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
356 // insert a new row, starting at position 0
357 insertRow(row, par, 0);
359 // and now append the whole paragraph behind the new row
362 appendParagraph(bview, firstrow);
364 row->next()->height(0);
365 appendParagraph(bview, row->next());
370 Inset * LyXText::getInset() const
373 if (cursor.pos() == 0 && cursor.par()->bibkey) {
374 inset = cursor.par()->bibkey;
375 } else if (cursor.pos() < cursor.par()->size()
376 && cursor.par()->isInset(cursor.pos())) {
377 inset = cursor.par()->getInset(cursor.pos());
383 void LyXText::toggleInset(BufferView * bview)
385 Inset * inset = getInset();
386 // is there an editable inset at cursor position?
387 if (!isEditableInset(inset)) {
388 // No, try to see if we are inside a collapsable inset
389 if (inset_owner && inset_owner->owner()
390 && inset_owner->owner()->isOpen()) {
391 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
392 inset_owner->owner()->close(bview);
393 bview->getLyXText()->cursorRight(bview);
397 //bview->owner()->message(inset->editMessage());
399 // do we want to keep this?? (JMarc)
400 if (!isHighlyEditableInset(inset))
401 setCursorParUndo(bview);
403 if (inset->isOpen()) {
409 inset->open(bview, !inset->isOpen());
414 /* used in setlayout */
415 // Asger is not sure we want to do this...
416 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
419 LyXLayout_ptr const & layout = par->layout();
422 for (pos_type pos = 0; pos < par->size(); ++pos) {
423 if (pos < beginningOfMainBody(buf, par))
424 layoutfont = layout->labelfont;
426 layoutfont = layout->font;
428 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
429 tmpfont.reduce(layoutfont);
430 par->setFont(pos, tmpfont);
435 Paragraph * LyXText::setLayout(BufferView * bview,
436 LyXCursor & cur, LyXCursor & sstart_cur,
437 LyXCursor & send_cur,
438 string const & layout)
440 Paragraph * endpar = send_cur.par()->next();
441 Paragraph * undoendpar = endpar;
443 if (endpar && endpar->getDepth()) {
444 while (endpar && endpar->getDepth()) {
445 endpar = endpar->next();
449 endpar = endpar->next(); // because of parindents etc.
452 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
454 // ok we have a selection. This is always between sstart_cur
455 // and sel_end cursor
457 Paragraph * par = sstart_cur.par();
458 Paragraph * epar = send_cur.par()->next();
460 LyXLayout_ptr const & lyxlayout =
461 bview->buffer()->params.getLyXTextClass()[layout];
464 par->applyLayout(lyxlayout);
465 makeFontEntriesLayoutSpecific(bview->buffer(), par);
466 Paragraph * fppar = par;
467 fppar->params().spaceTop(lyxlayout->fill_top ?
468 VSpace(VSpace::VFILL)
469 : VSpace(VSpace::NONE));
470 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
471 VSpace(VSpace::VFILL)
472 : VSpace(VSpace::NONE));
473 if (lyxlayout->margintype == MARGIN_MANUAL)
474 par->setLabelWidthString(lyxlayout->labelstring());
475 if (lyxlayout->labeltype != LABEL_BIBLIO
477 delete fppar->bibkey;
482 } while (par != epar);
488 // set layout over selection and make a total rebreak of those paragraphs
489 void LyXText::setLayout(BufferView * bview, string const & layout)
491 LyXCursor tmpcursor = cursor; /* store the current cursor */
493 // if there is no selection just set the layout
494 // of the current paragraph */
495 if (!selection.set()) {
496 selection.start = cursor; // dummy selection
497 selection.end = cursor;
499 Paragraph * endpar = setLayout(bview, cursor, selection.start,
500 selection.end, layout);
501 redoParagraphs(bview, selection.start, endpar);
503 // we have to reset the selection, because the
504 // geometry could have changed
505 setCursor(bview, selection.start.par(),
506 selection.start.pos(), false);
507 selection.cursor = cursor;
508 setCursor(bview, selection.end.par(), selection.end.pos(), false);
509 updateCounters(bview);
512 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
516 // increment depth over selection and
517 // make a total rebreak of those paragraphs
518 void LyXText::incDepth(BufferView * bview)
520 // If there is no selection, just use the current paragraph
521 if (!selection.set()) {
522 selection.start = cursor; // dummy selection
523 selection.end = cursor;
526 // We end at the next paragraph with depth 0
527 Paragraph * endpar = selection.end.par()->next();
529 Paragraph * undoendpar = endpar;
531 if (endpar && endpar->getDepth()) {
532 while (endpar && endpar->getDepth()) {
533 endpar = endpar->next();
537 endpar = endpar->next(); // because of parindents etc.
540 setUndo(bview, Undo::EDIT,
541 selection.start.par(), undoendpar);
543 LyXCursor tmpcursor = cursor; // store the current cursor
545 // ok we have a selection. This is always between sel_start_cursor
546 // and sel_end cursor
547 cursor = selection.start;
549 bool anything_changed = false;
552 // NOTE: you can't change the depth of a bibliography entry
553 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
554 Paragraph * prev = cursor.par()->previous();
557 if (cursor.par()->getDepth()
558 < prev->getMaxDepthAfter()) {
559 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
560 anything_changed = true;
564 if (cursor.par() == selection.end.par())
566 cursor.par(cursor.par()->next());
569 // if nothing changed set all depth to 0
570 if (!anything_changed) {
571 cursor = selection.start;
572 while (cursor.par() != selection.end.par()) {
573 cursor.par()->params().depth(0);
574 cursor.par(cursor.par()->next());
576 cursor.par()->params().depth(0);
579 redoParagraphs(bview, selection.start, endpar);
581 // we have to reset the selection, because the
582 // geometry could have changed
583 setCursor(bview, selection.start.par(), selection.start.pos());
584 selection.cursor = cursor;
585 setCursor(bview, selection.end.par(), selection.end.pos());
586 updateCounters(bview);
589 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
593 // decrement depth over selection and
594 // make a total rebreak of those paragraphs
595 void LyXText::decDepth(BufferView * bview)
597 // if there is no selection just set the layout
598 // of the current paragraph
599 if (!selection.set()) {
600 selection.start = cursor; // dummy selection
601 selection.end = cursor;
603 Paragraph * endpar = selection.end.par()->next();
604 Paragraph * undoendpar = endpar;
606 if (endpar && endpar->getDepth()) {
607 while (endpar && endpar->getDepth()) {
608 endpar = endpar->next();
612 endpar = endpar->next(); // because of parindents etc.
615 setUndo(bview, Undo::EDIT,
616 selection.start.par(), undoendpar);
618 LyXCursor tmpcursor = cursor; // store the current cursor
620 // ok we have a selection. This is always between sel_start_cursor
621 // and sel_end cursor
622 cursor = selection.start;
625 if (cursor.par()->params().depth()) {
626 cursor.par()->params()
627 .depth(cursor.par()->params().depth() - 1);
629 if (cursor.par() == selection.end.par()) {
632 cursor.par(cursor.par()->next());
635 redoParagraphs(bview, selection.start, endpar);
637 // we have to reset the selection, because the
638 // geometry could have changed
639 setCursor(bview, selection.start.par(),
640 selection.start.pos());
641 selection.cursor = cursor;
642 setCursor(bview, selection.end.par(), selection.end.pos());
643 updateCounters(bview);
646 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
650 // set font over selection and make a total rebreak of those paragraphs
651 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
653 // if there is no selection just set the current_font
654 if (!selection.set()) {
655 // Determine basis font
657 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
659 layoutfont = getLabelFont(bview->buffer(),
662 layoutfont = getLayoutFont(bview->buffer(),
665 // Update current font
666 real_current_font.update(font,
667 bview->buffer()->params.language,
670 // Reduce to implicit settings
671 current_font = real_current_font;
672 current_font.reduce(layoutfont);
673 // And resolve it completely
674 real_current_font.realize(layoutfont);
679 LyXCursor tmpcursor = cursor; // store the current cursor
681 // ok we have a selection. This is always between sel_start_cursor
682 // and sel_end cursor
684 setUndo(bview, Undo::EDIT,
685 selection.start.par(), selection.end.par()->next());
687 cursor = selection.start;
688 while (cursor.par() != selection.end.par() ||
689 cursor.pos() < selection.end.pos())
691 if (cursor.pos() < cursor.par()->size()) {
692 // an open footnote should behave like a closed one
693 setCharFont(bview, cursor.par(), cursor.pos(),
695 cursor.pos(cursor.pos() + 1);
698 cursor.par(cursor.par()->next());
703 redoParagraphs(bview, selection.start, selection.end.par()->next());
705 // we have to reset the selection, because the
706 // geometry could have changed, but we keep
707 // it for user convenience
708 setCursor(bview, selection.start.par(), selection.start.pos());
709 selection.cursor = cursor;
710 setCursor(bview, selection.end.par(), selection.end.pos());
712 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
713 tmpcursor.boundary());
717 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
719 Row * tmprow = cur.row();
720 int y = cur.y() - tmprow->baseline();
722 setHeightOfRow(bview, tmprow);
724 while (tmprow->previous()
725 && tmprow->previous()->par() == tmprow->par()) {
726 tmprow = tmprow->previous();
727 y -= tmprow->height();
728 setHeightOfRow(bview, tmprow);
731 // we can set the refreshing parameters now
732 status(bview, LyXText::NEED_MORE_REFRESH);
734 refresh_row = tmprow;
735 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
739 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
741 Row * tmprow = cur.row();
743 int y = cur.y() - tmprow->baseline();
744 setHeightOfRow(bview, tmprow);
746 while (tmprow->previous()
747 && tmprow->previous()->par() == tmprow->par()) {
748 tmprow = tmprow->previous();
749 y -= tmprow->height();
752 // we can set the refreshing parameters now
753 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
755 refresh_row = tmprow;
757 status(bview, LyXText::NEED_MORE_REFRESH);
758 setCursor(bview, cur.par(), cur.pos());
762 // deletes and inserts again all paragaphs between the cursor
763 // and the specified par
764 // This function is needed after SetLayout and SetFont etc.
765 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
766 Paragraph const * endpar) const
769 Paragraph * tmppar = 0;
770 Paragraph * first_phys_par = 0;
772 Row * tmprow = cur.row();
774 int y = cur.y() - tmprow->baseline();
776 if (!tmprow->previous()) {
777 // a trick/hack for UNDO
778 // This is needed because in an UNDO/REDO we could have changed
779 // the ownerParagrah() so the paragraph inside the row is NOT
780 // my really first par anymore. Got it Lars ;) (Jug 20011206)
781 first_phys_par = ownerParagraph();
783 first_phys_par = tmprow->par();
784 while (tmprow->previous()
785 && tmprow->previous()->par() == first_phys_par)
787 tmprow = tmprow->previous();
788 y -= tmprow->height();
792 // we can set the refreshing parameters now
793 status(bview, LyXText::NEED_MORE_REFRESH);
795 refresh_row = tmprow->previous(); /* the real refresh row will
796 be deleted, so I store
800 tmppar = tmprow->next()->par();
803 while (tmprow->next() && tmppar != endpar) {
804 removeRow(tmprow->next());
805 if (tmprow->next()) {
806 tmppar = tmprow->next()->par();
812 // remove the first one
813 tmprow2 = tmprow; /* this is because tmprow->previous()
815 tmprow = tmprow->previous();
818 tmppar = first_phys_par;
822 insertParagraph(bview, tmppar, tmprow);
826 while (tmprow->next()
827 && tmprow->next()->par() == tmppar) {
828 tmprow = tmprow->next();
830 tmppar = tmppar->next();
832 } while (tmppar && tmppar != endpar);
834 // this is because of layout changes
836 refresh_y -= refresh_row->height();
837 setHeightOfRow(bview, refresh_row);
839 refresh_row = firstrow;
841 setHeightOfRow(bview, refresh_row);
844 if (tmprow && tmprow->next())
845 setHeightOfRow(bview, tmprow->next());
846 updateCounters(bview);
850 void LyXText::fullRebreak(BufferView * bview)
856 if (need_break_row) {
857 breakAgain(bview, need_break_row);
864 // important for the screen
867 // the cursor set functions have a special mechanism. When they
868 // realize, that you left an empty paragraph, they will delete it.
869 // They also delete the corresponding row
871 // need the selection cursor:
872 void LyXText::setSelection(BufferView * bview)
874 bool const lsel = selection.set();
876 if (!selection.set()) {
877 last_sel_cursor = selection.cursor;
878 selection.start = selection.cursor;
879 selection.end = selection.cursor;
884 // first the toggling area
885 if (cursor.y() < last_sel_cursor.y()
886 || (cursor.y() == last_sel_cursor.y()
887 && cursor.x() < last_sel_cursor.x())) {
888 toggle_end_cursor = last_sel_cursor;
889 toggle_cursor = cursor;
891 toggle_end_cursor = cursor;
892 toggle_cursor = last_sel_cursor;
895 last_sel_cursor = cursor;
897 // and now the whole selection
899 if (selection.cursor.par() == cursor.par())
900 if (selection.cursor.pos() < cursor.pos()) {
901 selection.end = cursor;
902 selection.start = selection.cursor;
904 selection.end = selection.cursor;
905 selection.start = cursor;
907 else if (selection.cursor.y() < cursor.y() ||
908 (selection.cursor.y() == cursor.y()
909 && selection.cursor.x() < cursor.x())) {
910 selection.end = cursor;
911 selection.start = selection.cursor;
914 selection.end = selection.cursor;
915 selection.start = cursor;
918 // a selection with no contents is not a selection
919 if (selection.start.par() == selection.end.par() &&
920 selection.start.pos() == selection.end.pos())
921 selection.set(false);
923 if (inset_owner && (selection.set() || lsel))
924 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
928 string const LyXText::selectionAsString(Buffer const * buffer,
931 if (!selection.set()) return string();
933 // should be const ...
934 Paragraph * startpar(selection.start.par());
935 Paragraph * endpar(selection.end.par());
936 pos_type const startpos(selection.start.pos());
937 pos_type const endpos(selection.end.pos());
939 if (startpar == endpar) {
940 return startpar->asString(buffer, startpos, endpos, label);
945 // First paragraph in selection
946 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
948 // The paragraphs in between (if any)
949 LyXCursor tmpcur(selection.start);
950 tmpcur.par(tmpcur.par()->next());
951 while (tmpcur.par() != endpar) {
952 result += tmpcur.par()->asString(buffer, 0,
953 tmpcur.par()->size(),
955 tmpcur.par(tmpcur.par()->next());
958 // Last paragraph in selection
959 result += endpar->asString(buffer, 0, endpos, label);
965 void LyXText::clearSelection() const
967 selection.set(false);
968 selection.mark(false);
969 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
970 // reset this in the bv_owner!
971 if (bv_owner && bv_owner->text)
972 bv_owner->text->xsel_cache.set(false);
976 void LyXText::cursorHome(BufferView * bview) const
978 setCursor(bview, cursor.par(), cursor.row()->pos());
982 void LyXText::cursorEnd(BufferView * bview) const
984 if (!cursor.row()->next()
985 || cursor.row()->next()->par() != cursor.row()->par()) {
986 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
988 if (!cursor.par()->empty() &&
989 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
990 || cursor.par()->isNewline(rowLast(cursor.row())))) {
991 setCursor(bview, cursor.par(), rowLast(cursor.row()));
993 setCursor(bview,cursor.par(),
994 rowLast(cursor.row()) + 1);
1000 void LyXText::cursorTop(BufferView * bview) const
1002 while (cursor.par()->previous())
1003 cursor.par(cursor.par()->previous());
1004 setCursor(bview, cursor.par(), 0);
1008 void LyXText::cursorBottom(BufferView * bview) const
1010 while (cursor.par()->next())
1011 cursor.par(cursor.par()->next());
1012 setCursor(bview, cursor.par(), cursor.par()->size());
1016 void LyXText::toggleFree(BufferView * bview,
1017 LyXFont const & font, bool toggleall)
1019 // If the mask is completely neutral, tell user
1020 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1021 // Could only happen with user style
1022 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1026 // Try implicit word selection
1027 // If there is a change in the language the implicit word selection
1029 LyXCursor resetCursor = cursor;
1030 bool implicitSelection = (font.language() == ignore_language
1031 && font.number() == LyXFont::IGNORE)
1032 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1035 setFont(bview, font, toggleall);
1037 // Implicit selections are cleared afterwards
1038 //and cursor is set to the original position.
1039 if (implicitSelection) {
1041 cursor = resetCursor;
1042 setCursor(bview, cursor.par(), cursor.pos());
1043 selection.cursor = cursor;
1046 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1050 string LyXText::getStringToIndex(BufferView * bview)
1054 // Try implicit word selection
1055 // If there is a change in the language the implicit word selection
1057 LyXCursor const reset_cursor = cursor;
1058 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1060 if (!selection.set()) {
1061 bview->owner()->message(_("Nothing to index!"));
1064 if (selection.start.par() != selection.end.par()) {
1065 bview->owner()->message(_("Cannot index more than one paragraph!"));
1069 idxstring = selectionAsString(bview->buffer(), false);
1071 // Implicit selections are cleared afterwards
1072 //and cursor is set to the original position.
1073 if (implicitSelection) {
1075 cursor = reset_cursor;
1076 setCursor(bview, cursor.par(), cursor.pos());
1077 selection.cursor = cursor;
1083 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1084 Paragraph const * par) const
1086 if (par->layout()->labeltype != LABEL_MANUAL)
1089 return par->beginningOfMainBody();
1093 // the DTP switches for paragraphs. LyX will store them in the first
1094 // physicla paragraph. When a paragraph is broken, the top settings rest,
1095 // the bottom settings are given to the new one. So I can make shure,
1096 // they do not duplicate themself and you cannnot make dirty things with
1099 void LyXText::setParagraph(BufferView * bview,
1100 bool line_top, bool line_bottom,
1101 bool pagebreak_top, bool pagebreak_bottom,
1102 VSpace const & space_top,
1103 VSpace const & space_bottom,
1104 Spacing const & spacing,
1106 string labelwidthstring,
1109 LyXCursor tmpcursor = cursor;
1110 if (!selection.set()) {
1111 selection.start = cursor;
1112 selection.end = cursor;
1115 // make sure that the depth behind the selection are restored, too
1116 Paragraph * endpar = selection.end.par()->next();
1117 Paragraph * undoendpar = endpar;
1119 if (endpar && endpar->getDepth()) {
1120 while (endpar && endpar->getDepth()) {
1121 endpar = endpar->next();
1122 undoendpar = endpar;
1126 // because of parindents etc.
1127 endpar = endpar->next();
1130 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1133 Paragraph * tmppar = selection.end.par();
1135 while (tmppar != selection.start.par()->previous()) {
1136 setCursor(bview, tmppar, 0);
1137 status(bview, LyXText::NEED_MORE_REFRESH);
1138 refresh_row = cursor.row();
1139 refresh_y = cursor.y() - cursor.row()->baseline();
1140 cursor.par()->params().lineTop(line_top);
1141 cursor.par()->params().lineBottom(line_bottom);
1142 cursor.par()->params().pagebreakTop(pagebreak_top);
1143 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1144 cursor.par()->params().spaceTop(space_top);
1145 cursor.par()->params().spaceBottom(space_bottom);
1146 cursor.par()->params().spacing(spacing);
1147 // does the layout allow the new alignment?
1148 LyXLayout_ptr const & layout = cursor.par()->layout();
1150 if (align == LYX_ALIGN_LAYOUT)
1151 align = layout->align;
1152 if (align & layout->alignpossible) {
1153 if (align == layout->align)
1154 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1156 cursor.par()->params().align(align);
1158 cursor.par()->setLabelWidthString(labelwidthstring);
1159 cursor.par()->params().noindent(noindent);
1160 tmppar = cursor.par()->previous();
1163 redoParagraphs(bview, selection.start, endpar);
1166 setCursor(bview, selection.start.par(), selection.start.pos());
1167 selection.cursor = cursor;
1168 setCursor(bview, selection.end.par(), selection.end.pos());
1169 setSelection(bview);
1170 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1172 bview->updateInset(inset_owner, true);
1176 // set the counter of a paragraph. This includes the labels
1177 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1179 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1180 LyXLayout_ptr const & layout = par->layout();
1182 if (par->previous()) {
1184 par->params().appendix(par->previous()->params().appendix());
1185 if (!par->params().appendix() && par->params().startOfAppendix()) {
1186 par->params().appendix(true);
1187 textclass.counters().reset();
1189 par->enumdepth = par->previous()->enumdepth;
1190 par->itemdepth = par->previous()->itemdepth;
1192 par->params().appendix(par->params().startOfAppendix());
1197 /* Maybe we have to increment the enumeration depth.
1198 * BUT, enumeration in a footnote is considered in isolation from its
1199 * surrounding paragraph so don't increment if this is the
1200 * first line of the footnote
1201 * AND, bibliographies can't have their depth changed ie. they
1202 * are always of depth 0
1205 && par->previous()->getDepth() < par->getDepth()
1206 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1207 && par->enumdepth < 3
1208 && layout->labeltype != LABEL_BIBLIO) {
1212 // Maybe we have to decrement the enumeration depth, see note above
1214 && par->previous()->getDepth() > par->getDepth()
1215 && layout->labeltype != LABEL_BIBLIO) {
1216 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1219 if (!par->params().labelString().empty()) {
1220 par->params().labelString(string());
1223 if (layout->margintype == MARGIN_MANUAL) {
1224 if (par->params().labelWidthString().empty()) {
1225 par->setLabelWidthString(layout->labelstring());
1228 par->setLabelWidthString(string());
1231 // is it a layout that has an automatic label?
1232 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1234 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1239 if (i >= 0 && i <= buf->params.secnumdepth) {
1241 textclass.counters().step(layout->latexname());
1243 // Is there a label? Useful for Chapter layout
1244 if (!par->params().appendix()) {
1245 if (!layout->labelstring().empty())
1246 par->params().labelString(layout->labelstring());
1248 par->params().labelString(string());
1250 if (!layout->labelstring_appendix().empty())
1251 par->params().labelString(layout->labelstring_appendix());
1253 par->params().labelString(string());
1256 // Use if an integer is here less than elegant. For now.
1257 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1258 if (!par->params().appendix()) {
1259 numbertype = "sectioning";
1261 numbertype = "appendix";
1262 if (par->isRightToLeftPar(buf->params))
1263 langtype = "hebrew";
1268 s << textclass.counters()
1269 .numberLabel(layout->latexname(),
1270 numbertype, langtype, head);
1272 par->params().labelString(par->params().labelString() + s.str().c_str());
1273 // We really want to remove the c_str as soon as
1276 // reset enum counters
1277 textclass.counters().reset("enum");
1278 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1279 textclass.counters().reset("enum");
1280 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1282 // Yes I know this is a really, really! bad solution
1284 string enumcounter("enum");
1286 switch (par->enumdepth) {
1295 enumcounter += "iv";
1298 // not a valid enumdepth...
1302 textclass.counters().step(enumcounter);
1304 s << textclass.counters()
1305 .numberLabel(enumcounter,
1306 "enumeration", langtype);
1307 par->params().labelString(s.str().c_str());
1309 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1310 textclass.counters().step("bibitem");
1311 int number = textclass.counters().value("bibitem");
1313 InsetCommandParams p("bibitem");
1314 par->bibkey = new InsetBibKey(p);
1316 par->bibkey->setCounter(number);
1317 par->params().labelString(layout->labelstring());
1319 // In biblio should't be following counters but...
1321 string s = layout->labelstring();
1323 // the caption hack:
1324 if (layout->labeltype == LABEL_SENSITIVE) {
1325 Paragraph * tmppar = par;
1328 while (tmppar && tmppar->inInset()
1329 // the single '=' is intended below
1330 && (in = tmppar->inInset()->owner())) {
1331 if (in->lyxCode() == Inset::FLOAT_CODE) {
1335 tmppar = in->parOwner();
1341 = textclass.floats().getType(static_cast<InsetFloat*>(in)->type());
1343 textclass.counters().step(fl.type());
1345 // Doesn't work... yet.
1347 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1348 o << fl.name() << " #:";
1351 // par->SetLayout(0);
1352 // s = layout->labelstring;
1353 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1354 ? " :úåòîùî øñç" : "Senseless: ";
1357 par->params().labelString(s);
1359 // reset the enumeration counter. They are always reset
1360 // when there is any other layout between
1361 // Just fall-through between the cases so that all
1362 // enum counters deeper than enumdepth is also reset.
1363 switch (par->enumdepth) {
1365 textclass.counters().reset("enumi");
1367 textclass.counters().reset("enumii");
1369 textclass.counters().reset("enumiii");
1371 textclass.counters().reset("enumiv");
1377 // Updates all counters BEHIND the row. Changed paragraphs
1378 // with a dynamic left margin will be rebroken.
1379 void LyXText::updateCounters(BufferView * bview) const
1383 Row * row = firstrow;
1386 // CHECK if this is really needed. (Lgb)
1387 bview->buffer()->params.getLyXTextClass().counters().reset();
1390 while (row->par() != par)
1393 setCounter(bview->buffer(), par);
1395 // now check for the headline layouts. remember that they
1396 // have a dynamic left margin
1397 LyXLayout_ptr const & layout = par->layout();
1399 if (layout->margintype == MARGIN_DYNAMIC
1400 || layout->labeltype == LABEL_SENSITIVE) {
1401 // Rebreak the paragraph
1402 removeParagraph(row);
1403 appendParagraph(bview, row);
1410 void LyXText::insertInset(BufferView * bview, Inset * inset)
1412 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1414 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1416 cursor.par()->insertInset(cursor.pos(), inset);
1417 // Just to rebreak and refresh correctly.
1418 // The character will not be inserted a second time
1419 insertChar(bview, Paragraph::META_INSET);
1420 // If we enter a highly editable inset the cursor should be to before
1421 // the inset. This couldn't happen before as Undo was not handled inside
1422 // inset now after the Undo LyX tries to call inset->Edit(...) again
1423 // and cannot do this as the cursor is behind the inset and GetInset
1424 // does not return the inset!
1425 if (isHighlyEditableInset(inset)) {
1426 cursorLeft(bview, true);
1432 void LyXText::copyEnvironmentType()
1434 copylayouttype = cursor.par()->layout()->name();
1438 void LyXText::pasteEnvironmentType(BufferView * bview)
1440 setLayout(bview, copylayouttype);
1444 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1446 // Stuff what we got on the clipboard. Even if there is no selection.
1448 // There is a problem with having the stuffing here in that the
1449 // larger the selection the slower LyX will get. This can be
1450 // solved by running the line below only when the selection has
1451 // finished. The solution used currently just works, to make it
1452 // faster we need to be more clever and probably also have more
1453 // calls to stuffClipboard. (Lgb)
1454 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1456 // This doesn't make sense, if there is no selection
1457 if (!selection.set())
1460 // OK, we have a selection. This is always between selection.start
1461 // and selection.end
1463 // make sure that the depth behind the selection are restored, too
1464 Paragraph * endpar = selection.end.par()->next();
1465 Paragraph * undoendpar = endpar;
1467 if (endpar && endpar->getDepth()) {
1468 while (endpar && endpar->getDepth()) {
1469 endpar = endpar->next();
1470 undoendpar = endpar;
1472 } else if (endpar) {
1473 endpar = endpar->next(); // because of parindents etc.
1476 setUndo(bview, Undo::DELETE,
1477 selection.start.par(), undoendpar);
1479 // there are two cases: cut only within one paragraph or
1480 // more than one paragraph
1481 if (selection.start.par() == selection.end.par()) {
1482 // only within one paragraph
1483 endpar = selection.end.par();
1484 int pos = selection.end.pos();
1485 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1486 selection.start.pos(), pos,
1487 bview->buffer()->params.textclass,
1489 selection.end.pos(pos);
1491 endpar = selection.end.par();
1492 int pos = selection.end.pos();
1493 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1494 selection.start.pos(), pos,
1495 bview->buffer()->params.textclass,
1498 selection.end.par(endpar);
1499 selection.end.pos(pos);
1500 cursor.pos(selection.end.pos());
1502 endpar = endpar->next();
1504 // sometimes necessary
1506 selection.start.par()->stripLeadingSpaces();
1508 redoParagraphs(bview, selection.start, endpar);
1510 // cutSelection can invalidate the cursor so we need to set
1512 cursor = selection.start;
1514 // need a valid cursor. (Lgb)
1517 setCursor(bview, cursor.par(), cursor.pos());
1518 selection.cursor = cursor;
1519 updateCounters(bview);
1523 void LyXText::copySelection(BufferView * bview)
1525 // stuff the selection onto the X clipboard, from an explicit copy request
1526 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1528 // this doesnt make sense, if there is no selection
1529 if (!selection.set())
1532 // ok we have a selection. This is always between selection.start
1533 // and sel_end cursor
1535 // copy behind a space if there is one
1536 while (selection.start.par()->size() > selection.start.pos()
1537 && selection.start.par()->isLineSeparator(selection.start.pos())
1538 && (selection.start.par() != selection.end.par()
1539 || selection.start.pos() < selection.end.pos()))
1540 selection.start.pos(selection.start.pos() + 1);
1542 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1543 selection.start.pos(), selection.end.pos(),
1544 bview->buffer()->params.textclass);
1548 void LyXText::pasteSelection(BufferView * bview)
1550 // this does not make sense, if there is nothing to paste
1551 if (!CutAndPaste::checkPastePossible(cursor.par()))
1554 setUndo(bview, Undo::INSERT,
1555 cursor.par(), cursor.par()->next());
1558 Paragraph * actpar = cursor.par();
1559 int pos = cursor.pos();
1561 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1562 bview->buffer()->params.textclass);
1564 redoParagraphs(bview, cursor, endpar);
1566 setCursor(bview, cursor.par(), cursor.pos());
1569 selection.cursor = cursor;
1570 setCursor(bview, actpar, pos);
1571 setSelection(bview);
1572 updateCounters(bview);
1576 // sets the selection over the number of characters of string, no check!!
1577 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1582 selection.cursor = cursor;
1583 for (string::size_type i = 0; i < str.length(); ++i)
1585 setSelection(bview);
1589 // simple replacing. The font of the first selected character is used
1590 void LyXText::replaceSelectionWithString(BufferView * bview,
1593 setCursorParUndo(bview);
1596 if (!selection.set()) { // create a dummy selection
1597 selection.end = cursor;
1598 selection.start = cursor;
1601 // Get font setting before we cut
1602 pos_type pos = selection.end.pos();
1603 LyXFont const font = selection.start.par()
1604 ->getFontSettings(bview->buffer()->params,
1605 selection.start.pos());
1607 // Insert the new string
1608 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1609 selection.end.par()->insertChar(pos, (*cit), font);
1613 // Cut the selection
1614 cutSelection(bview, true, false);
1620 // needed to insert the selection
1621 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1623 Paragraph * par = cursor.par();
1624 pos_type pos = cursor.pos();
1625 Paragraph * endpar = cursor.par()->next();
1627 setCursorParUndo(bview);
1629 // only to be sure, should not be neccessary
1632 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1634 redoParagraphs(bview, cursor, endpar);
1635 setCursor(bview, cursor.par(), cursor.pos());
1636 selection.cursor = cursor;
1637 setCursor(bview, par, pos);
1638 setSelection(bview);
1642 // turns double-CR to single CR, others where converted into one
1643 // blank. Then InsertStringAsLines is called
1644 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1646 string linestr(str);
1647 bool newline_inserted = false;
1648 for (string::size_type i = 0; i < linestr.length(); ++i) {
1649 if (linestr[i] == '\n') {
1650 if (newline_inserted) {
1651 // we know that \r will be ignored by
1652 // InsertStringA. Of course, it is a dirty
1653 // trick, but it works...
1654 linestr[i - 1] = '\r';
1658 newline_inserted = true;
1660 } else if (IsPrintable(linestr[i])) {
1661 newline_inserted = false;
1664 insertStringAsLines(bview, linestr);
1668 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1671 LyXCursor tmpcursor;
1675 Row * row = getRow(par, pos, y);
1677 // is there a break one row above
1678 if (row->previous() && row->previous()->par() == row->par()) {
1679 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1680 if (z >= row->pos()) {
1681 // set the dimensions of the row above
1682 y -= row->previous()->height();
1684 refresh_row = row->previous();
1685 status(bview, LyXText::NEED_MORE_REFRESH);
1687 breakAgain(bview, row->previous());
1689 // set the cursor again. Otherwise
1690 // dangling pointers are possible
1691 setCursor(bview, cursor.par(), cursor.pos(),
1692 false, cursor.boundary());
1693 selection.cursor = cursor;
1698 int const tmpheight = row->height();
1699 pos_type const tmplast = rowLast(row);
1703 breakAgain(bview, row);
1704 if (row->height() == tmpheight && rowLast(row) == tmplast)
1705 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1707 status(bview, LyXText::NEED_MORE_REFRESH);
1709 // check the special right address boxes
1710 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1717 redoDrawingOfParagraph(bview, tmpcursor);
1720 // set the cursor again. Otherwise dangling pointers are possible
1721 // also set the selection
1723 if (selection.set()) {
1725 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1726 false, selection.cursor.boundary());
1727 selection.cursor = cursor;
1728 setCursorIntern(bview, selection.start.par(),
1729 selection.start.pos(),
1730 false, selection.start.boundary());
1731 selection.start = cursor;
1732 setCursorIntern(bview, selection.end.par(),
1733 selection.end.pos(),
1734 false, selection.end.boundary());
1735 selection.end = cursor;
1736 setCursorIntern(bview, last_sel_cursor.par(),
1737 last_sel_cursor.pos(),
1738 false, last_sel_cursor.boundary());
1739 last_sel_cursor = cursor;
1742 setCursorIntern(bview, cursor.par(), cursor.pos(),
1743 false, cursor.boundary());
1747 // returns false if inset wasn't found
1748 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1750 // first check the current paragraph
1751 int pos = cursor.par()->getPositionOfInset(inset);
1753 checkParagraph(bview, cursor.par(), pos);
1757 // check every paragraph
1759 Paragraph * par = ownerParagraph();
1761 pos = par->getPositionOfInset(inset);
1763 checkParagraph(bview, par, pos);
1773 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1775 bool setfont, bool boundary) const
1777 LyXCursor old_cursor = cursor;
1778 setCursorIntern(bview, par, pos, setfont, boundary);
1779 return deleteEmptyParagraphMechanism(bview, old_cursor);
1783 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1784 pos_type pos, bool boundary) const
1791 cur.boundary(boundary);
1793 // get the cursor y position in text
1795 Row * row = getRow(par, pos, y);
1796 Row * old_row = row;
1798 // if we are before the first char of this row and are still in the
1799 // same paragraph and there is a previous row then put the cursor on
1800 // the end of the previous row
1801 cur.iy(y + row->baseline());
1803 if (row->previous() && pos &&
1804 row->previous()->par() == row->par() &&
1805 par->getChar(pos) == Paragraph::META_INSET &&
1806 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1808 row = row->previous();
1813 // y is now the beginning of the cursor row
1814 y += row->baseline();
1815 // y is now the cursor baseline
1818 pos_type last = rowLastPrintable(old_row);
1820 if (pos > last + 1) {
1821 // This shouldn't happen.
1824 } else if (pos < row->pos()) {
1829 // now get the cursors x position
1830 float x = getCursorX(bview, row, pos, last, boundary);
1833 if (old_row != row) {
1834 x = getCursorX(bview, old_row, pos, last, boundary);
1841 float LyXText::getCursorX(BufferView * bview, Row * row,
1842 pos_type pos, pos_type last, bool boundary) const
1844 pos_type cursor_vpos = 0;
1846 float fill_separator;
1848 float fill_label_hfill;
1849 // This call HAS to be here because of the BidiTables!!!
1850 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1853 if (last < row->pos())
1854 cursor_vpos = row->pos();
1855 else if (pos > last && !boundary)
1856 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1857 ? row->pos() : last + 1;
1858 else if (pos > row->pos() &&
1859 (pos > last || boundary))
1860 /// Place cursor after char at (logical) position pos - 1
1861 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1862 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1864 /// Place cursor before char at (logical) position pos
1865 cursor_vpos = (bidi_level(pos) % 2 == 0)
1866 ? log2vis(pos) : log2vis(pos) + 1;
1868 pos_type main_body =
1869 beginningOfMainBody(bview->buffer(), row->par());
1870 if ((main_body > 0) &&
1871 ((main_body-1 > last) ||
1872 !row->par()->isLineSeparator(main_body-1)))
1875 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1876 pos_type pos = vis2log(vpos);
1877 if (main_body > 0 && pos == main_body - 1) {
1878 x += fill_label_hfill +
1879 font_metrics::width(
1880 row->par()->layout()->labelsep,
1881 getLabelFont(bview->buffer(),
1883 if (row->par()->isLineSeparator(main_body - 1))
1884 x -= singleWidth(bview,
1885 row->par(), main_body - 1);
1887 if (hfillExpansion(bview->buffer(), row, pos)) {
1888 x += singleWidth(bview, row->par(), pos);
1889 if (pos >= main_body)
1892 x += fill_label_hfill;
1893 } else if (row->par()->isSeparator(pos)) {
1894 x += singleWidth(bview, row->par(), pos);
1895 if (pos >= main_body)
1896 x += fill_separator;
1898 x += singleWidth(bview, row->par(), pos);
1904 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1905 pos_type pos, bool setfont, bool boundary) const
1907 InsetText * it = static_cast<InsetText *>(par->inInset());
1909 if (it != inset_owner) {
1910 lyxerr[Debug::INSETS] << "InsetText is " << it
1912 << "inset_owner is "
1913 << inset_owner << endl;
1914 #ifdef WITH_WARNINGS
1915 #warning I believe this code is wrong. (Lgb)
1916 #warning Jürgen, have a look at this. (Lgb)
1917 #warning Hmmm, I guess you are right but we
1918 #warning should verify when this is needed
1920 // Jürgen, would you like to have a look?
1921 // I guess we need to move the outer cursor
1922 // and open and lock the inset (bla bla bla)
1923 // stuff I don't know... so can you have a look?
1925 // I moved the lyxerr stuff in here so we can see if
1926 // this is actually really needed and where!
1928 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1933 setCursor(bview, cursor, par, pos, boundary);
1935 setCurrentFont(bview);
1939 void LyXText::setCurrentFont(BufferView * bview) const
1941 pos_type pos = cursor.pos();
1942 if (cursor.boundary() && pos > 0)
1946 if (pos == cursor.par()->size())
1948 else // potentional bug... BUG (Lgb)
1949 if (cursor.par()->isSeparator(pos)) {
1950 if (pos > cursor.row()->pos() &&
1951 bidi_level(pos) % 2 ==
1952 bidi_level(pos - 1) % 2)
1954 else if (pos + 1 < cursor.par()->size())
1960 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1961 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1963 if (cursor.pos() == cursor.par()->size() &&
1964 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1965 !cursor.boundary()) {
1966 Language const * lang =
1967 cursor.par()->getParLanguage(bview->buffer()->params);
1968 current_font.setLanguage(lang);
1969 current_font.setNumber(LyXFont::OFF);
1970 real_current_font.setLanguage(lang);
1971 real_current_font.setNumber(LyXFont::OFF);
1976 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
1978 LyXCursor old_cursor = cursor;
1980 setCursorFromCoordinates(bview, cursor, x, y);
1981 setCurrentFont(bview);
1982 deleteEmptyParagraphMechanism(bview, old_cursor);
1989 * return true if the cursor given is at the end of a row,
1990 * and the next row is filled by an inset that spans an entire
1993 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
1996 Row const & next = *row.next();
1998 if (next.pos() != cur.pos() || next.par() != cur.par())
2000 if (!cur.par()->isInset(cur.pos()))
2002 Inset const * inset = cur.par()->getInset(cur.pos());
2003 if (inset->needFullRow() || inset->display())
2010 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2013 // Get the row first.
2015 Row * row = getRowNearY(y);
2017 pos_type const column = getColumnNearX(bview, row, x, bound);
2018 cur.par(row->par());
2019 cur.pos(row->pos() + column);
2021 cur.y(y + row->baseline());
2024 if (beforeFullRowInset(*row, cur)) {
2025 pos_type last = rowLastPrintable(row);
2026 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2028 cur.iy(y + row->height() + row->next()->baseline());
2029 cur.irow(row->next());
2035 cur.boundary(bound);
2039 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2041 if (cursor.pos() > 0) {
2042 bool boundary = cursor.boundary();
2043 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2044 if (!internal && !boundary &&
2045 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2046 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2047 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2048 Paragraph * par = cursor.par()->previous();
2049 setCursor(bview, par, par->size());
2054 void LyXText::cursorRight(BufferView * bview, bool internal) const
2056 if (!internal && cursor.boundary() &&
2057 !cursor.par()->isNewline(cursor.pos()))
2058 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2059 else if (cursor.pos() < cursor.par()->size()) {
2060 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2062 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2063 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2064 } else if (cursor.par()->next())
2065 setCursor(bview, cursor.par()->next(), 0);
2069 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2072 int x = cursor.x_fix();
2073 int y = cursor.y() - cursor.row()->baseline() - 1;
2074 setCursorFromCoordinates(bview, x, y);
2076 int y1 = cursor.iy() - first_y;
2079 Inset * inset_hit = checkInsetHit(bview, x, y1);
2080 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2081 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2085 setCursorFromCoordinates(bview, cursor.x_fix(),
2086 cursor.y() - cursor.row()->baseline() - 1);
2091 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2094 int x = cursor.x_fix();
2095 int y = cursor.y() - cursor.row()->baseline() +
2096 cursor.row()->height() + 1;
2097 setCursorFromCoordinates(bview, x, y);
2098 if (!selecting && cursor.row() == cursor.irow()) {
2099 int y1 = cursor.iy() - first_y;
2102 Inset * inset_hit = checkInsetHit(bview, x, y1);
2103 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2104 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2108 setCursorFromCoordinates(bview, cursor.x_fix(),
2109 cursor.y() - cursor.row()->baseline()
2110 + cursor.row()->height() + 1);
2115 void LyXText::cursorUpParagraph(BufferView * bview) const
2117 if (cursor.pos() > 0) {
2118 setCursor(bview, cursor.par(), 0);
2120 else if (cursor.par()->previous()) {
2121 setCursor(bview, cursor.par()->previous(), 0);
2126 void LyXText::cursorDownParagraph(BufferView * bview) const
2128 if (cursor.par()->next()) {
2129 setCursor(bview, cursor.par()->next(), 0);
2131 setCursor(bview, cursor.par(), cursor.par()->size());
2135 // fix the cursor `cur' after a characters has been deleted at `where'
2136 // position. Called by deleteEmptyParagraphMechanism
2137 void LyXText::fixCursorAfterDelete(BufferView * bview,
2139 LyXCursor const & where) const
2141 // if cursor is not in the paragraph where the delete occured,
2143 if (cur.par() != where.par())
2146 // if cursor position is after the place where the delete occured,
2148 if (cur.pos() > where.pos())
2149 cur.pos(cur.pos()-1);
2151 // check also if we don't want to set the cursor on a spot behind the
2152 // pagragraph because we erased the last character.
2153 if (cur.pos() > cur.par()->size())
2154 cur.pos(cur.par()->size());
2156 // recompute row et al. for this cursor
2157 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2161 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2162 LyXCursor const & old_cursor) const
2164 // Would be wrong to delete anything if we have a selection.
2165 if (selection.set())
2168 // We allow all kinds of "mumbo-jumbo" when freespacing.
2169 if (old_cursor.par()->layout()->free_spacing
2170 || old_cursor.par()->isFreeSpacing()) {
2174 /* Ok I'll put some comments here about what is missing.
2175 I have fixed BackSpace (and thus Delete) to not delete
2176 double-spaces automagically. I have also changed Cut,
2177 Copy and Paste to hopefully do some sensible things.
2178 There are still some small problems that can lead to
2179 double spaces stored in the document file or space at
2180 the beginning of paragraphs. This happens if you have
2181 the cursor betwenn to spaces and then save. Or if you
2182 cut and paste and the selection have a space at the
2183 beginning and then save right after the paste. I am
2184 sure none of these are very hard to fix, but I will
2185 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2186 that I can get some feedback. (Lgb)
2189 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2190 // delete the LineSeparator.
2193 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2194 // delete the LineSeparator.
2197 // If the pos around the old_cursor were spaces, delete one of them.
2198 if (old_cursor.par() != cursor.par()
2199 || old_cursor.pos() != cursor.pos()) {
2200 // Only if the cursor has really moved
2202 if (old_cursor.pos() > 0
2203 && old_cursor.pos() < old_cursor.par()->size()
2204 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2205 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2206 old_cursor.par()->erase(old_cursor.pos() - 1);
2207 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2209 #ifdef WITH_WARNINGS
2210 #warning This will not work anymore when we have multiple views of the same buffer
2211 // In this case, we will have to correct also the cursors held by
2212 // other bufferviews. It will probably be easier to do that in a more
2213 // automated way in LyXCursor code. (JMarc 26/09/2001)
2215 // correct all cursors held by the LyXText
2216 fixCursorAfterDelete(bview, cursor, old_cursor);
2217 fixCursorAfterDelete(bview, selection.cursor,
2219 fixCursorAfterDelete(bview, selection.start,
2221 fixCursorAfterDelete(bview, selection.end, old_cursor);
2222 fixCursorAfterDelete(bview, last_sel_cursor,
2224 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2225 fixCursorAfterDelete(bview, toggle_end_cursor,
2231 // don't delete anything if this is the ONLY paragraph!
2232 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2235 // Do not delete empty paragraphs with keepempty set.
2236 if (old_cursor.par()->layout()->keepempty)
2239 // only do our magic if we changed paragraph
2240 if (old_cursor.par() == cursor.par())
2243 // record if we have deleted a paragraph
2244 // we can't possibly have deleted a paragraph before this point
2245 bool deleted = false;
2247 if ((old_cursor.par()->empty()
2248 || (old_cursor.par()->size() == 1
2249 && old_cursor.par()->isLineSeparator(0)))) {
2250 // ok, we will delete anything
2251 LyXCursor tmpcursor;
2253 // make sure that you do not delete any environments
2254 status(bview, LyXText::NEED_MORE_REFRESH);
2257 if (old_cursor.row()->previous()) {
2258 refresh_row = old_cursor.row()->previous();
2259 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2261 cursor = old_cursor; // that undo can restore the right cursor position
2262 Paragraph * endpar = old_cursor.par()->next();
2263 if (endpar && endpar->getDepth()) {
2264 while (endpar && endpar->getDepth()) {
2265 endpar = endpar->next();
2268 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2272 removeRow(old_cursor.row());
2273 if (ownerParagraph() == old_cursor.par()) {
2274 ownerParagraph(ownerParagraph()->next());
2277 delete old_cursor.par();
2279 /* Breakagain the next par. Needed because of
2280 * the parindent that can occur or dissappear.
2281 * The next row can change its height, if
2282 * there is another layout before */
2283 if (refresh_row->next()) {
2284 breakAgain(bview, refresh_row->next());
2285 updateCounters(bview);
2287 setHeightOfRow(bview, refresh_row);
2289 refresh_row = old_cursor.row()->next();
2290 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2293 cursor = old_cursor; // that undo can restore the right cursor position
2294 Paragraph * endpar = old_cursor.par()->next();
2295 if (endpar && endpar->getDepth()) {
2296 while (endpar && endpar->getDepth()) {
2297 endpar = endpar->next();
2300 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2304 removeRow(old_cursor.row());
2306 if (ownerParagraph() == old_cursor.par()) {
2307 ownerParagraph(ownerParagraph()->next());
2310 delete old_cursor.par();
2312 /* Breakagain the next par. Needed because of
2313 the parindent that can occur or dissappear.
2314 The next row can change its height, if
2315 there is another layout before */
2317 breakAgain(bview, refresh_row);
2318 updateCounters(bview);
2323 setCursorIntern(bview, cursor.par(), cursor.pos());
2325 if (selection.cursor.par() == old_cursor.par()
2326 && selection.cursor.pos() == old_cursor.pos()) {
2327 // correct selection
2328 selection.cursor = cursor;
2332 if (old_cursor.par()->stripLeadingSpaces()) {
2333 redoParagraphs(bview, old_cursor,
2334 old_cursor.par()->next());
2336 setCursorIntern(bview, cursor.par(), cursor.pos());
2337 selection.cursor = cursor;
2344 Paragraph * LyXText::ownerParagraph() const
2347 return inset_owner->paragraph();
2349 return &*(bv_owner->buffer()->paragraphs.begin());
2353 void LyXText::ownerParagraph(Paragraph * p) const
2356 inset_owner->paragraph(p);
2358 bv_owner->buffer()->paragraphs.set(p);
2363 void LyXText::ownerParagraph(int id, Paragraph * p) const
2365 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2366 if (op && op->inInset()) {
2367 static_cast<InsetText *>(op->inInset())->paragraph(p);
2374 LyXText::text_status LyXText::status() const
2380 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2382 LyXText * t = bview->text;
2384 // We should only go up with refreshing code so this means that if
2385 // we have a MORE refresh we should never set it to LITTLE if we still
2386 // didn't handle it (and then it will be UNCHANGED. Now as long as
2387 // we stay inside one LyXText this may work but we need to tell the
2388 // outermost LyXText that it should REALLY draw us if there is some
2389 // change in a Inset::LyXText. So you see that when we are inside a
2390 // inset's LyXText we give the LITTLE to the outermost LyXText to
2391 // tell'em that it should redraw the actual row (where the inset
2392 // resides! Capito?!
2394 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2396 if (inset_owner && st != UNCHANGED) {
2397 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2398 if (!t->refresh_row) {
2399 t->refresh_row = t->cursor.row();
2400 t->refresh_y = t->cursor.y() -
2401 t->cursor.row()->baseline();