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 "lyxtextclasslist.h"
21 #include "frontends/LyXView.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
27 #include "CutAndPaste.h"
28 #include "frontends/Painter.h"
29 #include "frontends/font_metrics.h"
33 #include "FloatList.h"
35 #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"
56 LyXText::LyXText(BufferView * bv)
57 : number_of_rows(0), height(0), width(0), first_y(0),
58 bv_owner(bv), inset_owner(0), the_locking_inset(0),
59 need_break_row(0), refresh_y(0), refresh_row(0),
60 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
64 LyXText::LyXText(InsetText * inset)
65 : number_of_rows(0), height(0), width(0), first_y(0),
66 bv_owner(0), inset_owner(inset), the_locking_inset(0),
67 need_break_row(0), refresh_y(0), refresh_row(0),
68 status_(LyXText::UNCHANGED), 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();
87 copylayouttype.erase();
88 number_of_rows = first_y = refresh_y = 0;
89 status_ = LyXText::UNCHANGED;
93 Paragraph * par = ownerParagraph();
94 current_font = getFont(bview->buffer(), par, 0);
97 insertParagraph(bview, par, lastrow);
100 setCursorIntern(bview, firstrow->par(), 0);
101 selection.cursor = cursor;
107 // Delete all rows, this does not touch the paragraphs!
108 Row * tmprow = firstrow;
110 tmprow = firstrow->next();
119 LyXFont const realizeFont(LyXFont const & font,
123 LyXFont tmpfont(font);
124 Paragraph::depth_type par_depth = par->getDepth();
126 // Resolve against environment font information
127 while (par && par_depth && !tmpfont.resolved()) {
128 par = par->outerHook();
130 #ifndef INHERIT_LANGUAGE
131 tmpfont.realize(par->layout()->font);
133 tmpfont.realize(textclasslist.
134 Style(buf->params.textclass,
135 par->layout())->font,
136 buf->params.language);
138 par_depth = par->getDepth();
142 #ifndef INHERIT_LANGUAGE
143 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
145 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
146 buf->params.language);
155 // Gets the fully instantiated font at a given position in a paragraph
156 // Basically the same routine as Paragraph::getFont() in paragraph.C.
157 // The difference is that this one is used for displaying, and thus we
158 // are allowed to make cosmetic improvements. For instance make footnotes
160 // If position is -1, we get the layout font of the paragraph.
161 // If position is -2, we get the font of the manual label of the paragraph.
162 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
165 lyx::Assert(pos >= 0);
167 LyXLayout_ptr const & layout = par->layout();
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout->labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params, pos);
177 par->inInset()->getDrawFont(f);
178 #ifndef INHERIT_LANGUAGE
179 return f.realize(layout->reslabelfont);
181 return f.realize(layout.reslabelfont, buf->params.language);
184 LyXFont f = par->getFontSettings(buf->params, pos);
186 par->inInset()->getDrawFont(f);
187 #ifndef INHERIT_LANGUAGE
188 return f.realize(layout->resfont);
190 return f.realize(layout.resfont, buf->params.language);
195 // The uncommon case need not be optimized as much
199 if (pos < beginningOfMainBody(buf, par)) {
201 layoutfont = layout->labelfont;
204 layoutfont = layout->font;
207 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
208 #ifndef INHERIT_LANGUAGE
209 tmpfont.realize(layoutfont);
211 tmpfont.realize(layoutfont, buf->params.language);
214 par->inInset()->getDrawFont(tmpfont);
216 return realizeFont(tmpfont, buf, par);
220 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
222 LyXLayout_ptr const & layout = par->layout();
224 Paragraph::depth_type par_depth = par->getDepth();
227 return layout->resfont;
230 return realizeFont(layout->font, buf, par);
234 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
236 LyXLayout_ptr const & layout = par->layout();
238 Paragraph::depth_type par_depth = par->getDepth();
241 return layout->reslabelfont;
244 return realizeFont(layout->labelfont, buf, par);
248 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
249 pos_type pos, LyXFont const & fnt,
252 Buffer const * buf = bv->buffer();
253 LyXFont font = getFont(buf, par, pos);
254 font.update(fnt, buf->params.language, toggleall);
255 // Let the insets convert their font
256 if (par->isInset(pos)) {
257 Inset * inset = par->getInset(pos);
258 if (isEditableInset(inset)) {
259 UpdatableInset * uinset =
260 static_cast<UpdatableInset *>(inset);
261 uinset->setFont(bv, fnt, toggleall, true);
265 // Plug thru to version below:
266 setCharFont(buf, par, pos, font);
270 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
271 pos_type pos, LyXFont const & fnt)
275 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
276 LyXLayout_ptr const & layout = par->layout();
278 // Get concrete layout font to reduce against
281 if (pos < beginningOfMainBody(buf, par))
282 layoutfont = layout->labelfont;
284 layoutfont = layout->font;
286 // Realize against environment font information
287 if (par->getDepth()) {
288 Paragraph * tp = par;
289 while (!layoutfont.resolved() && tp && tp->getDepth()) {
290 tp = tp->outerHook();
292 #ifndef INHERIT_LANGUAGE
293 layoutfont.realize(tp->layout()->font);
295 layoutfont.realize(textclasslist.
296 Style(buf->params.textclass,
298 buf->params.language);
303 #ifndef INHERIT_LANGUAGE
304 layoutfont.realize(tclass.defaultfont());
306 layoutfont.realize(tclass.defaultfont(), buf->params.language);
309 // Now, reduce font against full layout font
310 font.reduce(layoutfont);
312 par->setFont(pos, font);
316 // inserts a new row behind the specified row, increments
317 // the touched counters
318 void LyXText::insertRow(Row * row, Paragraph * par,
321 Row * tmprow = new Row;
324 tmprow->next(firstrow);
327 tmprow->previous(row);
328 tmprow->next(row->next());
333 tmprow->next()->previous(tmprow);
335 if (tmprow->previous())
336 tmprow->previous()->next(tmprow);
348 // removes the row and reset the touched counters
349 void LyXText::removeRow(Row * row) const
351 Row * row_prev = row->previous();
353 row->next()->previous(row_prev);
355 firstrow = row->next();
356 // lyx::Assert(firstrow);
358 row_prev->next(row->next());
360 if (row == lastrow) {
361 lyx::Assert(!row->next());
364 if (refresh_row == row) {
365 refresh_row = row_prev ? row_prev : row->next();
366 // what about refresh_y, refresh_height
369 height -= row->height(); // the text becomes smaller
372 --number_of_rows; // one row less
376 // remove all following rows of the paragraph of the specified row.
377 void LyXText::removeParagraph(Row * row) const
379 Paragraph * tmppar = row->par();
383 while (row && row->par() == tmppar) {
384 tmprow = row->next();
391 // insert the specified paragraph behind the specified row
392 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
395 insertRow(row, par, 0); /* insert a new row, starting
398 setCounter(bview->buffer(), par); // set the counters
400 // and now append the whole paragraph behind the new row
403 appendParagraph(bview, firstrow);
405 row->next()->height(0);
406 appendParagraph(bview, row->next());
411 Inset * LyXText::getInset() const
414 if (cursor.pos() == 0 && cursor.par()->bibkey) {
415 inset = cursor.par()->bibkey;
416 } else if (cursor.pos() < cursor.par()->size()
417 && cursor.par()->isInset(cursor.pos())) {
418 inset = cursor.par()->getInset(cursor.pos());
424 void LyXText::toggleInset(BufferView * bview)
426 Inset * inset = getInset();
427 // is there an editable inset at cursor position?
428 if (!isEditableInset(inset)) {
429 // No, try to see if we are inside a collapsable inset
430 if (inset_owner && inset_owner->owner()
431 && inset_owner->owner()->isOpen()) {
432 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
433 inset_owner->owner()->close(bview);
437 //bview->owner()->message(inset->editMessage());
439 // do we want to keep this?? (JMarc)
440 if (!isHighlyEditableInset(inset))
441 setCursorParUndo(bview);
443 if (inset->isOpen()) {
449 inset->open(bview, !inset->isOpen());
454 /* used in setlayout */
455 // Asger is not sure we want to do this...
456 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
459 LyXLayout_ptr const & layout = par->layout();
462 for (pos_type pos = 0; pos < par->size(); ++pos) {
463 if (pos < beginningOfMainBody(buf, par))
464 layoutfont = layout->labelfont;
466 layoutfont = layout->font;
468 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
469 tmpfont.reduce(layoutfont);
470 par->setFont(pos, tmpfont);
475 Paragraph * LyXText::setLayout(BufferView * bview,
476 LyXCursor & cur, LyXCursor & sstart_cur,
477 LyXCursor & send_cur,
478 string const & layout)
480 Paragraph * endpar = send_cur.par()->next();
481 Paragraph * undoendpar = endpar;
483 if (endpar && endpar->getDepth()) {
484 while (endpar && endpar->getDepth()) {
485 endpar = endpar->next();
489 endpar = endpar->next(); // because of parindents etc.
492 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
494 // ok we have a selection. This is always between sstart_cur
495 // and sel_end cursor
497 Paragraph * par = sstart_cur.par();
498 Paragraph * epar = send_cur.par()->next();
500 LyXLayout_ptr const & lyxlayout =
501 textclasslist[bview->buffer()->params.textclass][layout];
504 par->applyLayout(lyxlayout);
505 makeFontEntriesLayoutSpecific(bview->buffer(), par);
506 Paragraph * fppar = par;
507 fppar->params().spaceTop(lyxlayout->fill_top ?
508 VSpace(VSpace::VFILL)
509 : VSpace(VSpace::NONE));
510 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
511 VSpace(VSpace::VFILL)
512 : VSpace(VSpace::NONE));
513 if (lyxlayout->margintype == MARGIN_MANUAL)
514 par->setLabelWidthString(lyxlayout->labelstring());
515 if (lyxlayout->labeltype != LABEL_BIBLIO
517 delete fppar->bibkey;
522 } while (par != epar);
528 // set layout over selection and make a total rebreak of those paragraphs
529 void LyXText::setLayout(BufferView * bview, string const & layout)
531 LyXCursor tmpcursor = cursor; /* store the current cursor */
533 // if there is no selection just set the layout
534 // of the current paragraph */
535 if (!selection.set()) {
536 selection.start = cursor; // dummy selection
537 selection.end = cursor;
539 Paragraph * endpar = setLayout(bview, cursor, selection.start,
540 selection.end, layout);
541 redoParagraphs(bview, selection.start, endpar);
543 // we have to reset the selection, because the
544 // geometry could have changed
545 setCursor(bview, selection.start.par(),
546 selection.start.pos(), false);
547 selection.cursor = cursor;
548 setCursor(bview, selection.end.par(), selection.end.pos(), false);
549 updateCounters(bview, cursor.row());
552 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
556 // increment depth over selection and
557 // make a total rebreak of those paragraphs
558 void LyXText::incDepth(BufferView * bview)
560 // If there is no selection, just use the current paragraph
561 if (!selection.set()) {
562 selection.start = cursor; // dummy selection
563 selection.end = cursor;
566 // We end at the next paragraph with depth 0
567 Paragraph * endpar = selection.end.par()->next();
569 Paragraph * undoendpar = endpar;
571 if (endpar && endpar->getDepth()) {
572 while (endpar && endpar->getDepth()) {
573 endpar = endpar->next();
577 endpar = endpar->next(); // because of parindents etc.
580 setUndo(bview, Undo::EDIT,
581 selection.start.par(), undoendpar);
583 LyXCursor tmpcursor = cursor; // store the current cursor
585 // ok we have a selection. This is always between sel_start_cursor
586 // and sel_end cursor
587 cursor = selection.start;
589 bool anything_changed = false;
592 // NOTE: you can't change the depth of a bibliography entry
593 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
594 Paragraph * prev = cursor.par()->previous();
597 if (cursor.par()->getDepth()
598 < prev->getMaxDepthAfter()) {
599 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
600 anything_changed = true;
604 if (cursor.par() == selection.end.par())
606 cursor.par(cursor.par()->next());
609 // if nothing changed set all depth to 0
610 if (!anything_changed) {
611 cursor = selection.start;
612 while (cursor.par() != selection.end.par()) {
613 cursor.par()->params().depth(0);
614 cursor.par(cursor.par()->next());
616 cursor.par()->params().depth(0);
619 redoParagraphs(bview, selection.start, endpar);
621 // we have to reset the selection, because the
622 // geometry could have changed
623 setCursor(bview, selection.start.par(), selection.start.pos());
624 selection.cursor = cursor;
625 setCursor(bview, selection.end.par(), selection.end.pos());
626 updateCounters(bview, cursor.row());
629 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
633 // decrement depth over selection and
634 // make a total rebreak of those paragraphs
635 void LyXText::decDepth(BufferView * bview)
637 // if there is no selection just set the layout
638 // of the current paragraph
639 if (!selection.set()) {
640 selection.start = cursor; // dummy selection
641 selection.end = cursor;
643 Paragraph * endpar = selection.end.par()->next();
644 Paragraph * undoendpar = endpar;
646 if (endpar && endpar->getDepth()) {
647 while (endpar && endpar->getDepth()) {
648 endpar = endpar->next();
652 endpar = endpar->next(); // because of parindents etc.
655 setUndo(bview, Undo::EDIT,
656 selection.start.par(), undoendpar);
658 LyXCursor tmpcursor = cursor; // store the current cursor
660 // ok we have a selection. This is always between sel_start_cursor
661 // and sel_end cursor
662 cursor = selection.start;
665 if (cursor.par()->params().depth()) {
666 cursor.par()->params()
667 .depth(cursor.par()->params().depth() - 1);
669 if (cursor.par() == selection.end.par()) {
672 cursor.par(cursor.par()->next());
675 redoParagraphs(bview, selection.start, endpar);
677 // we have to reset the selection, because the
678 // geometry could have changed
679 setCursor(bview, selection.start.par(),
680 selection.start.pos());
681 selection.cursor = cursor;
682 setCursor(bview, selection.end.par(), selection.end.pos());
683 updateCounters(bview, cursor.row());
686 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
690 // set font over selection and make a total rebreak of those paragraphs
691 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
693 // if there is no selection just set the current_font
694 if (!selection.set()) {
695 // Determine basis font
697 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
699 layoutfont = getLabelFont(bview->buffer(),
702 layoutfont = getLayoutFont(bview->buffer(),
705 // Update current font
706 real_current_font.update(font,
707 bview->buffer()->params.language,
710 // Reduce to implicit settings
711 current_font = real_current_font;
712 current_font.reduce(layoutfont);
713 // And resolve it completely
714 #ifndef INHERIT_LANGUAGE
715 real_current_font.realize(layoutfont);
717 real_current_font.realize(layoutfont,
718 bview->buffer()->params.language);
723 LyXCursor tmpcursor = cursor; // store the current cursor
725 // ok we have a selection. This is always between sel_start_cursor
726 // and sel_end cursor
728 setUndo(bview, Undo::EDIT,
729 selection.start.par(), selection.end.par()->next());
731 cursor = selection.start;
732 while (cursor.par() != selection.end.par() ||
733 cursor.pos() < selection.end.pos())
735 if (cursor.pos() < cursor.par()->size()) {
736 // an open footnote should behave like a closed one
737 setCharFont(bview, cursor.par(), cursor.pos(),
739 cursor.pos(cursor.pos() + 1);
742 cursor.par(cursor.par()->next());
747 redoParagraphs(bview, selection.start, selection.end.par()->next());
749 // we have to reset the selection, because the
750 // geometry could have changed, but we keep
751 // it for user convenience
752 setCursor(bview, selection.start.par(), selection.start.pos());
753 selection.cursor = cursor;
754 setCursor(bview, selection.end.par(), selection.end.pos());
756 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
757 tmpcursor.boundary());
761 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
763 Row * tmprow = cur.row();
764 int y = cur.y() - tmprow->baseline();
766 setHeightOfRow(bview, tmprow);
768 while (tmprow->previous()
769 && tmprow->previous()->par() == tmprow->par()) {
770 tmprow = tmprow->previous();
771 y -= tmprow->height();
772 setHeightOfRow(bview, tmprow);
775 // we can set the refreshing parameters now
776 status(bview, LyXText::NEED_MORE_REFRESH);
778 refresh_row = tmprow;
779 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
783 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
785 Row * tmprow = cur.row();
787 int y = cur.y() - tmprow->baseline();
788 setHeightOfRow(bview, tmprow);
790 while (tmprow->previous()
791 && tmprow->previous()->par() == tmprow->par()) {
792 tmprow = tmprow->previous();
793 y -= tmprow->height();
796 // we can set the refreshing parameters now
797 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
799 refresh_row = tmprow;
801 status(bview, LyXText::NEED_MORE_REFRESH);
802 setCursor(bview, cur.par(), cur.pos());
806 // deletes and inserts again all paragaphs between the cursor
807 // and the specified par
808 // This function is needed after SetLayout and SetFont etc.
809 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
810 Paragraph const * endpar) const
813 Paragraph * tmppar = 0;
814 Paragraph * first_phys_par = 0;
816 Row * tmprow = cur.row();
818 int y = cur.y() - tmprow->baseline();
820 if (!tmprow->previous()) {
821 // a trick/hack for UNDO
822 // This is needed because in an UNDO/REDO we could have changed
823 // the ownerParagrah() so the paragraph inside the row is NOT
824 // my really first par anymore. Got it Lars ;) (Jug 20011206)
825 first_phys_par = ownerParagraph();
827 first_phys_par = tmprow->par();
828 while (tmprow->previous()
829 && tmprow->previous()->par() == first_phys_par)
831 tmprow = tmprow->previous();
832 y -= tmprow->height();
836 // we can set the refreshing parameters now
837 status(bview, LyXText::NEED_MORE_REFRESH);
839 refresh_row = tmprow->previous(); /* the real refresh row will
840 be deleted, so I store
844 tmppar = tmprow->next()->par();
847 while (tmprow->next() && tmppar != endpar) {
848 removeRow(tmprow->next());
849 if (tmprow->next()) {
850 tmppar = tmprow->next()->par();
856 // remove the first one
857 tmprow2 = tmprow; /* this is because tmprow->previous()
859 tmprow = tmprow->previous();
862 tmppar = first_phys_par;
866 insertParagraph(bview, tmppar, tmprow);
870 while (tmprow->next()
871 && tmprow->next()->par() == tmppar) {
872 tmprow = tmprow->next();
874 tmppar = tmppar->next();
876 } while (tmppar && tmppar != endpar);
878 // this is because of layout changes
880 refresh_y -= refresh_row->height();
881 setHeightOfRow(bview, refresh_row);
883 refresh_row = firstrow;
885 setHeightOfRow(bview, refresh_row);
888 if (tmprow && tmprow->next())
889 setHeightOfRow(bview, tmprow->next());
893 void LyXText::fullRebreak(BufferView * bview)
899 if (need_break_row) {
900 breakAgain(bview, need_break_row);
907 // important for the screen
910 // the cursor set functions have a special mechanism. When they
911 // realize, that you left an empty paragraph, they will delete it.
912 // They also delete the corresponding row
914 // need the selection cursor:
915 void LyXText::setSelection(BufferView * bview)
917 bool const lsel = selection.set();
919 if (!selection.set()) {
920 last_sel_cursor = selection.cursor;
921 selection.start = selection.cursor;
922 selection.end = selection.cursor;
927 // first the toggling area
928 if (cursor.y() < last_sel_cursor.y()
929 || (cursor.y() == last_sel_cursor.y()
930 && cursor.x() < last_sel_cursor.x())) {
931 toggle_end_cursor = last_sel_cursor;
932 toggle_cursor = cursor;
934 toggle_end_cursor = cursor;
935 toggle_cursor = last_sel_cursor;
938 last_sel_cursor = cursor;
940 // and now the whole selection
942 if (selection.cursor.par() == cursor.par())
943 if (selection.cursor.pos() < cursor.pos()) {
944 selection.end = cursor;
945 selection.start = selection.cursor;
947 selection.end = selection.cursor;
948 selection.start = cursor;
950 else if (selection.cursor.y() < cursor.y() ||
951 (selection.cursor.y() == cursor.y()
952 && selection.cursor.x() < cursor.x())) {
953 selection.end = cursor;
954 selection.start = selection.cursor;
957 selection.end = selection.cursor;
958 selection.start = cursor;
961 // a selection with no contents is not a selection
962 if (selection.start.par() == selection.end.par() &&
963 selection.start.pos() == selection.end.pos())
964 selection.set(false);
966 if (inset_owner && (selection.set() || lsel))
967 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
971 string const LyXText::selectionAsString(Buffer const * buffer,
974 if (!selection.set()) return string();
977 // Special handling if the whole selection is within one paragraph
978 if (selection.start.par() == selection.end.par()) {
979 result += selection.start.par()->asString(buffer,
980 selection.start.pos(),
986 // The selection spans more than one paragraph
988 // First paragraph in selection
989 result += selection.start.par()->asString(buffer,
990 selection.start.pos(),
991 selection.start.par()->size(),
995 // The paragraphs in between (if any)
996 LyXCursor tmpcur(selection.start);
997 tmpcur.par(tmpcur.par()->next());
998 while (tmpcur.par() != selection.end.par()) {
999 result += tmpcur.par()->asString(buffer, 0,
1000 tmpcur.par()->size(),
1002 tmpcur.par(tmpcur.par()->next());
1005 // Last paragraph in selection
1006 result += selection.end.par()->asString(buffer, 0,
1007 selection.end.pos(), label);
1013 void LyXText::clearSelection() const
1015 selection.set(false);
1016 selection.mark(false);
1017 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1018 // reset this in the bv_owner!
1019 if (bv_owner && bv_owner->text)
1020 bv_owner->text->xsel_cache.set(false);
1024 void LyXText::cursorHome(BufferView * bview) const
1026 setCursor(bview, cursor.par(), cursor.row()->pos());
1030 void LyXText::cursorEnd(BufferView * bview) const
1032 if (!cursor.row()->next()
1033 || cursor.row()->next()->par() != cursor.row()->par()) {
1034 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1036 if (cursor.par()->size() &&
1037 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1038 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1039 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1041 setCursor(bview,cursor.par(),
1042 rowLast(cursor.row()) + 1);
1048 void LyXText::cursorTop(BufferView * bview) const
1050 while (cursor.par()->previous())
1051 cursor.par(cursor.par()->previous());
1052 setCursor(bview, cursor.par(), 0);
1056 void LyXText::cursorBottom(BufferView * bview) const
1058 while (cursor.par()->next())
1059 cursor.par(cursor.par()->next());
1060 setCursor(bview, cursor.par(), cursor.par()->size());
1064 void LyXText::toggleFree(BufferView * bview,
1065 LyXFont const & font, bool toggleall)
1067 // If the mask is completely neutral, tell user
1068 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1069 // Could only happen with user style
1070 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1074 // Try implicit word selection
1075 // If there is a change in the language the implicit word selection
1077 LyXCursor resetCursor = cursor;
1078 bool implicitSelection = (font.language() == ignore_language
1079 && font.number() == LyXFont::IGNORE)
1080 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1083 setFont(bview, font, toggleall);
1085 // Implicit selections are cleared afterwards
1086 //and cursor is set to the original position.
1087 if (implicitSelection) {
1089 cursor = resetCursor;
1090 setCursor(bview, cursor.par(), cursor.pos());
1091 selection.cursor = cursor;
1094 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1098 string LyXText::getStringToIndex(BufferView * bview)
1102 // Try implicit word selection
1103 // If there is a change in the language the implicit word selection
1105 LyXCursor resetCursor = cursor;
1106 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1108 if (!selection.set()) {
1109 bview->owner()->message(_("Nothing to index!"));
1112 if (selection.start.par() != selection.end.par()) {
1113 bview->owner()->message(_("Cannot index more than one paragraph!"));
1117 idxstring = selectionAsString(bview->buffer(), false);
1119 // Implicit selections are cleared afterwards
1120 //and cursor is set to the original position.
1121 if (implicitSelection) {
1123 cursor = resetCursor;
1124 setCursor(bview, cursor.par(), cursor.pos());
1125 selection.cursor = cursor;
1131 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1132 Paragraph const * par) const
1134 if (par->layout()->labeltype != LABEL_MANUAL)
1137 return par->beginningOfMainBody();
1141 // the DTP switches for paragraphs. LyX will store them in the first
1142 // physicla paragraph. When a paragraph is broken, the top settings rest,
1143 // the bottom settings are given to the new one. So I can make shure,
1144 // they do not duplicate themself and you cannnot make dirty things with
1147 void LyXText::setParagraph(BufferView * bview,
1148 bool line_top, bool line_bottom,
1149 bool pagebreak_top, bool pagebreak_bottom,
1150 VSpace const & space_top,
1151 VSpace const & space_bottom,
1152 Spacing const & spacing,
1154 string labelwidthstring,
1157 LyXCursor tmpcursor = cursor;
1158 if (!selection.set()) {
1159 selection.start = cursor;
1160 selection.end = cursor;
1163 // make sure that the depth behind the selection are restored, too
1164 Paragraph * endpar = selection.end.par()->next();
1165 Paragraph * undoendpar = endpar;
1167 if (endpar && endpar->getDepth()) {
1168 while (endpar && endpar->getDepth()) {
1169 endpar = endpar->next();
1170 undoendpar = endpar;
1174 // because of parindents etc.
1175 endpar = endpar->next();
1178 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1181 Paragraph * tmppar = selection.end.par();
1183 while (tmppar != selection.start.par()->previous()) {
1184 setCursor(bview, tmppar, 0);
1185 status(bview, LyXText::NEED_MORE_REFRESH);
1186 refresh_row = cursor.row();
1187 refresh_y = cursor.y() - cursor.row()->baseline();
1188 cursor.par()->params().lineTop(line_top);
1189 cursor.par()->params().lineBottom(line_bottom);
1190 cursor.par()->params().pagebreakTop(pagebreak_top);
1191 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1192 cursor.par()->params().spaceTop(space_top);
1193 cursor.par()->params().spaceBottom(space_bottom);
1194 cursor.par()->params().spacing(spacing);
1195 // does the layout allow the new alignment?
1196 LyXLayout_ptr const & layout = cursor.par()->layout();
1198 if (align == LYX_ALIGN_LAYOUT)
1199 align = layout->align;
1200 if (align & layout->alignpossible) {
1201 if (align == layout->align)
1202 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1204 cursor.par()->params().align(align);
1206 cursor.par()->setLabelWidthString(labelwidthstring);
1207 cursor.par()->params().noindent(noindent);
1208 tmppar = cursor.par()->previous();
1211 redoParagraphs(bview, selection.start, endpar);
1214 setCursor(bview, selection.start.par(), selection.start.pos());
1215 selection.cursor = cursor;
1216 setCursor(bview, selection.end.par(), selection.end.pos());
1217 setSelection(bview);
1218 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1220 bview->updateInset(inset_owner, true);
1224 char loweralphaCounter(int n)
1226 if (n < 1 || n > 26)
1236 char alphaCounter(int n)
1238 if (n < 1 || n > 26)
1246 char hebrewCounter(int n)
1248 static const char hebrew[22] = {
1249 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1250 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1251 '÷', 'ø', 'ù', 'ú'
1253 if (n < 1 || n > 22)
1261 string const romanCounter(int n)
1263 static char const * roman[20] = {
1264 "i", "ii", "iii", "iv", "v",
1265 "vi", "vii", "viii", "ix", "x",
1266 "xi", "xii", "xiii", "xiv", "xv",
1267 "xvi", "xvii", "xviii", "xix", "xx"
1269 if (n < 1 || n > 20)
1278 // set the counter of a paragraph. This includes the labels
1279 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1281 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1282 LyXLayout_ptr const & layout = par->layout();
1284 // copy the prev-counters to this one,
1285 // unless this is the first paragraph
1286 if (par->previous()) {
1287 for (int i = 0; i < 10; ++i) {
1288 par->setCounter(i, par->previous()->getFirstCounter(i));
1290 par->params().appendix(par->previous()->params().appendix());
1291 if (!par->params().appendix() && par->params().startOfAppendix()) {
1292 par->params().appendix(true);
1293 for (int i = 0; i < 10; ++i) {
1294 par->setCounter(i, 0);
1297 par->enumdepth = par->previous()->enumdepth;
1298 par->itemdepth = par->previous()->itemdepth;
1300 for (int i = 0; i < 10; ++i) {
1301 par->setCounter(i, 0);
1303 par->params().appendix(par->params().startOfAppendix());
1308 /* Maybe we have to increment the enumeration depth.
1309 * BUT, enumeration in a footnote is considered in isolation from its
1310 * surrounding paragraph so don't increment if this is the
1311 * first line of the footnote
1312 * AND, bibliographies can't have their depth changed ie. they
1313 * are always of depth 0
1316 && par->previous()->getDepth() < par->getDepth()
1317 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1318 && par->enumdepth < 3
1319 && layout->labeltype != LABEL_BIBLIO) {
1323 // Maybe we have to decrement the enumeration depth, see note above
1325 && par->previous()->getDepth() > par->getDepth()
1326 && layout->labeltype != LABEL_BIBLIO) {
1327 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1328 par->setCounter(6 + par->enumdepth,
1329 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1330 // reset the counters.A depth change is like a breaking layout
1331 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1332 par->setCounter(i, 0);
1335 if (!par->params().labelString().empty()) {
1336 par->params().labelString(string());
1339 if (layout->margintype == MARGIN_MANUAL) {
1340 if (par->params().labelWidthString().empty()) {
1341 par->setLabelWidthString(layout->labelstring());
1344 par->setLabelWidthString(string());
1347 // is it a layout that has an automatic label?
1348 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1350 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1351 if (i >= 0 && i<= buf->params.secnumdepth) {
1352 par->incCounter(i); // increment the counter
1354 // Is there a label? Useful for Chapter layout
1355 if (!par->params().appendix()) {
1356 if (!layout->labelstring().empty())
1357 par->params().labelString(layout->labelstring());
1359 par->params().labelString(string());
1361 if (!layout->labelstring_appendix().empty())
1362 par->params().labelString(layout->labelstring_appendix());
1364 par->params().labelString(string());
1369 if (!par->params().appendix()) {
1370 switch (2 * LABEL_COUNTER_CHAPTER -
1371 textclass.maxcounter() + i) {
1372 case LABEL_COUNTER_CHAPTER:
1373 s << par->getCounter(i);
1375 case LABEL_COUNTER_SECTION:
1376 s << par->getCounter(i - 1) << '.'
1377 << par->getCounter(i);
1379 case LABEL_COUNTER_SUBSECTION:
1380 s << par->getCounter(i - 2) << '.'
1381 << par->getCounter(i - 1) << '.'
1382 << par->getCounter(i);
1384 case LABEL_COUNTER_SUBSUBSECTION:
1385 s << par->getCounter(i - 3) << '.'
1386 << par->getCounter(i - 2) << '.'
1387 << par->getCounter(i - 1) << '.'
1388 << par->getCounter(i);
1391 case LABEL_COUNTER_PARAGRAPH:
1392 s << par->getCounter(i - 4) << '.'
1393 << par->getCounter(i - 3) << '.'
1394 << par->getCounter(i - 2) << '.'
1395 << par->getCounter(i - 1) << '.'
1396 << par->getCounter(i);
1398 case LABEL_COUNTER_SUBPARAGRAPH:
1399 s << par->getCounter(i - 5) << '.'
1400 << par->getCounter(i - 4) << '.'
1401 << par->getCounter(i - 3) << '.'
1402 << par->getCounter(i - 2) << '.'
1403 << par->getCounter(i - 1) << '.'
1404 << par->getCounter(i);
1408 // Can this ever be reached? And in the
1409 // case it is, how can this be correct?
1411 s << par->getCounter(i) << '.';
1414 } else { // appendix
1415 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1416 case LABEL_COUNTER_CHAPTER:
1417 if (par->isRightToLeftPar(buf->params))
1418 s << hebrewCounter(par->getCounter(i));
1420 s << alphaCounter(par->getCounter(i));
1422 case LABEL_COUNTER_SECTION:
1423 if (par->isRightToLeftPar(buf->params))
1424 s << hebrewCounter(par->getCounter(i - 1));
1426 s << alphaCounter(par->getCounter(i - 1));
1429 << par->getCounter(i);
1432 case LABEL_COUNTER_SUBSECTION:
1433 if (par->isRightToLeftPar(buf->params))
1434 s << hebrewCounter(par->getCounter(i - 2));
1436 s << alphaCounter(par->getCounter(i - 2));
1439 << par->getCounter(i-1) << '.'
1440 << par->getCounter(i);
1443 case LABEL_COUNTER_SUBSUBSECTION:
1444 if (par->isRightToLeftPar(buf->params))
1445 s << hebrewCounter(par->getCounter(i-3));
1447 s << alphaCounter(par->getCounter(i-3));
1450 << par->getCounter(i-2) << '.'
1451 << par->getCounter(i-1) << '.'
1452 << par->getCounter(i);
1455 case LABEL_COUNTER_PARAGRAPH:
1456 if (par->isRightToLeftPar(buf->params))
1457 s << hebrewCounter(par->getCounter(i-4));
1459 s << alphaCounter(par->getCounter(i-4));
1462 << par->getCounter(i-3) << '.'
1463 << par->getCounter(i-2) << '.'
1464 << par->getCounter(i-1) << '.'
1465 << par->getCounter(i);
1468 case LABEL_COUNTER_SUBPARAGRAPH:
1469 if (par->isRightToLeftPar(buf->params))
1470 s << hebrewCounter(par->getCounter(i-5));
1472 s << alphaCounter(par->getCounter(i-5));
1475 << par->getCounter(i-4) << '.'
1476 << par->getCounter(i-3) << '.'
1477 << par->getCounter(i-2) << '.'
1478 << par->getCounter(i-1) << '.'
1479 << par->getCounter(i);
1483 // Can this ever be reached? And in the
1484 // case it is, how can this be correct?
1486 s << par->getCounter(i) << '.';
1492 par->params().labelString(par->params().labelString() +s.str().c_str());
1493 // We really want to remove the c_str as soon as
1496 for (i++; i < 10; ++i) {
1497 // reset the following counters
1498 par->setCounter(i, 0);
1500 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1501 for (i++; i < 10; ++i) {
1502 // reset the following counters
1503 par->setCounter(i, 0);
1505 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1506 par->incCounter(i + par->enumdepth);
1507 int number = par->getCounter(i + par->enumdepth);
1511 switch (par->enumdepth) {
1513 if (par->isRightToLeftPar(buf->params))
1515 << hebrewCounter(number)
1519 << loweralphaCounter(number)
1523 if (par->isRightToLeftPar(buf->params))
1524 s << '.' << romanCounter(number);
1526 s << romanCounter(number) << '.';
1529 if (par->isRightToLeftPar(buf->params))
1531 << alphaCounter(number);
1533 s << alphaCounter(number)
1537 if (par->isRightToLeftPar(buf->params))
1544 par->params().labelString(s.str().c_str());
1546 for (i += par->enumdepth + 1; i < 10; ++i) {
1547 // reset the following counters
1548 par->setCounter(i, 0);
1552 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1553 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1555 int number = par->getCounter(i);
1557 InsetCommandParams p("bibitem" );
1558 par->bibkey = new InsetBibKey(p);
1560 par->bibkey->setCounter(number);
1561 par->params().labelString(layout->labelstring());
1563 // In biblio should't be following counters but...
1565 string s = layout->labelstring();
1567 // the caption hack:
1568 if (layout->labeltype == LABEL_SENSITIVE) {
1569 bool isOK (par->inInset() && par->inInset()->owner() &&
1570 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1573 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1575 = floatList.getType(tmp->type());
1576 // We should get the correct number here too.
1577 s = fl.name() + " #:";
1579 /* par->SetLayout(0);
1580 s = layout->labelstring; */
1581 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1582 ? " :úåòîùî øñç" : "Senseless: ";
1585 par->params().labelString(s);
1587 /* reset the enumeration counter. They are always resetted
1588 * when there is any other layout between */
1589 for (int i = 6 + par->enumdepth; i < 10; ++i)
1590 par->setCounter(i, 0);
1595 // Updates all counters BEHIND the row. Changed paragraphs
1596 // with a dynamic left margin will be rebroken.
1597 void LyXText::updateCounters(BufferView * bview, Row * row) const
1605 par = row->par()->next();
1609 while (row->par() != par)
1612 setCounter(bview->buffer(), par);
1614 // now check for the headline layouts. remember that they
1615 // have a dynamic left margin
1616 LyXLayout_ptr const & layout = par->layout();
1618 if (layout->margintype == MARGIN_DYNAMIC
1619 || layout->labeltype == LABEL_SENSITIVE) {
1620 // Rebreak the paragraph
1621 removeParagraph(row);
1622 appendParagraph(bview, row);
1629 void LyXText::insertInset(BufferView * bview, Inset * inset)
1631 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1633 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1635 cursor.par()->insertInset(cursor.pos(), inset);
1636 // Just to rebreak and refresh correctly.
1637 // The character will not be inserted a second time
1638 insertChar(bview, Paragraph::META_INSET);
1639 // If we enter a highly editable inset the cursor should be to before
1640 // the inset. This couldn't happen before as Undo was not handled inside
1641 // inset now after the Undo LyX tries to call inset->Edit(...) again
1642 // and cannot do this as the cursor is behind the inset and GetInset
1643 // does not return the inset!
1644 if (isHighlyEditableInset(inset)) {
1645 cursorLeft(bview, true);
1651 void LyXText::copyEnvironmentType()
1653 copylayouttype = cursor.par()->layout()->name();
1657 void LyXText::pasteEnvironmentType(BufferView * bview)
1659 setLayout(bview, copylayouttype);
1663 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1665 // Stuff what we got on the clipboard. Even if there is no selection.
1667 // There is a problem with having the stuffing here in that the
1668 // larger the selection the slower LyX will get. This can be
1669 // solved by running the line below only when the selection has
1670 // finished. The solution used currently just works, to make it
1671 // faster we need to be more clever and probably also have more
1672 // calls to stuffClipboard. (Lgb)
1673 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1675 // This doesn't make sense, if there is no selection
1676 if (!selection.set())
1679 // OK, we have a selection. This is always between selection.start
1680 // and selection.end
1682 // make sure that the depth behind the selection are restored, too
1683 Paragraph * endpar = selection.end.par()->next();
1684 Paragraph * undoendpar = endpar;
1686 if (endpar && endpar->getDepth()) {
1687 while (endpar && endpar->getDepth()) {
1688 endpar = endpar->next();
1689 undoendpar = endpar;
1691 } else if (endpar) {
1692 endpar = endpar->next(); // because of parindents etc.
1695 setUndo(bview, Undo::DELETE,
1696 selection.start.par(), undoendpar);
1698 // there are two cases: cut only within one paragraph or
1699 // more than one paragraph
1700 if (selection.start.par() == selection.end.par()) {
1701 // only within one paragraph
1702 endpar = selection.end.par();
1703 int pos = selection.end.pos();
1704 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1705 selection.start.pos(), pos,
1706 bview->buffer()->params.textclass,
1708 selection.end.pos(pos);
1710 endpar = selection.end.par();
1711 int pos = selection.end.pos();
1712 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1713 selection.start.pos(), pos,
1714 bview->buffer()->params.textclass,
1717 selection.end.par(endpar);
1718 selection.end.pos(pos);
1719 cursor.pos(selection.end.pos());
1721 endpar = endpar->next();
1723 // sometimes necessary
1725 selection.start.par()->stripLeadingSpaces();
1727 redoParagraphs(bview, selection.start, endpar);
1729 // cutSelection can invalidate the cursor so we need to set
1731 cursor = selection.start;
1733 // need a valid cursor. (Lgb)
1736 setCursor(bview, cursor.par(), cursor.pos());
1737 selection.cursor = cursor;
1738 updateCounters(bview, cursor.row());
1742 void LyXText::copySelection(BufferView * bview)
1744 // stuff the selection onto the X clipboard, from an explicit copy request
1745 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1747 // this doesnt make sense, if there is no selection
1748 if (!selection.set())
1751 // ok we have a selection. This is always between selection.start
1752 // and sel_end cursor
1754 // copy behind a space if there is one
1755 while (selection.start.par()->size() > selection.start.pos()
1756 && selection.start.par()->isLineSeparator(selection.start.pos())
1757 && (selection.start.par() != selection.end.par()
1758 || selection.start.pos() < selection.end.pos()))
1759 selection.start.pos(selection.start.pos() + 1);
1761 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1762 selection.start.pos(), selection.end.pos(),
1763 bview->buffer()->params.textclass);
1767 void LyXText::pasteSelection(BufferView * bview)
1769 // this does not make sense, if there is nothing to paste
1770 if (!CutAndPaste::checkPastePossible(cursor.par()))
1773 setUndo(bview, Undo::INSERT,
1774 cursor.par(), cursor.par()->next());
1777 Paragraph * actpar = cursor.par();
1778 int pos = cursor.pos();
1780 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1781 bview->buffer()->params.textclass);
1783 redoParagraphs(bview, cursor, endpar);
1785 setCursor(bview, cursor.par(), cursor.pos());
1788 selection.cursor = cursor;
1789 setCursor(bview, actpar, pos);
1790 setSelection(bview);
1791 updateCounters(bview, cursor.row());
1795 // sets the selection over the number of characters of string, no check!!
1796 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1801 selection.cursor = cursor;
1802 for (string::size_type i = 0; i < str.length(); ++i)
1804 setSelection(bview);
1808 // simple replacing. The font of the first selected character is used
1809 void LyXText::replaceSelectionWithString(BufferView * bview,
1812 setCursorParUndo(bview);
1815 if (!selection.set()) { // create a dummy selection
1816 selection.end = cursor;
1817 selection.start = cursor;
1820 // Get font setting before we cut
1821 pos_type pos = selection.end.pos();
1822 LyXFont const font = selection.start.par()
1823 ->getFontSettings(bview->buffer()->params,
1824 selection.start.pos());
1826 // Insert the new string
1827 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1828 selection.end.par()->insertChar(pos, (*cit), font);
1832 // Cut the selection
1833 cutSelection(bview, true, false);
1839 // needed to insert the selection
1840 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1842 Paragraph * par = cursor.par();
1843 pos_type pos = cursor.pos();
1844 Paragraph * endpar = cursor.par()->next();
1846 setCursorParUndo(bview);
1848 // only to be sure, should not be neccessary
1851 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1853 redoParagraphs(bview, cursor, endpar);
1854 setCursor(bview, cursor.par(), cursor.pos());
1855 selection.cursor = cursor;
1856 setCursor(bview, par, pos);
1857 setSelection(bview);
1861 // turns double-CR to single CR, others where converted into one
1862 // blank. Then InsertStringAsLines is called
1863 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1865 string linestr(str);
1866 bool newline_inserted = false;
1867 for (string::size_type i = 0; i < linestr.length(); ++i) {
1868 if (linestr[i] == '\n') {
1869 if (newline_inserted) {
1870 // we know that \r will be ignored by
1871 // InsertStringA. Of course, it is a dirty
1872 // trick, but it works...
1873 linestr[i - 1] = '\r';
1877 newline_inserted = true;
1879 } else if (IsPrintable(linestr[i])) {
1880 newline_inserted = false;
1883 insertStringAsLines(bview, linestr);
1887 bool LyXText::gotoNextInset(BufferView * bview,
1888 vector<Inset::Code> const & codes,
1889 string const & contents) const
1891 LyXCursor res = cursor;
1894 if (res.pos() < res.par()->size() - 1) {
1895 res.pos(res.pos() + 1);
1897 res.par(res.par()->next());
1901 } while (res.par() &&
1902 !(res.par()->isInset(res.pos())
1903 && (inset = res.par()->getInset(res.pos())) != 0
1904 && find(codes.begin(), codes.end(), inset->lyxCode())
1906 && (contents.empty() ||
1907 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1911 setCursor(bview, res.par(), res.pos(), false);
1918 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1921 LyXCursor tmpcursor;
1925 Row * row = getRow(par, pos, y);
1927 // is there a break one row above
1928 if (row->previous() && row->previous()->par() == row->par()) {
1929 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1930 if (z >= row->pos()) {
1931 // set the dimensions of the row above
1932 y -= row->previous()->height();
1934 refresh_row = row->previous();
1935 status(bview, LyXText::NEED_MORE_REFRESH);
1937 breakAgain(bview, row->previous());
1939 // set the cursor again. Otherwise
1940 // dangling pointers are possible
1941 setCursor(bview, cursor.par(), cursor.pos(),
1942 false, cursor.boundary());
1943 selection.cursor = cursor;
1948 int const tmpheight = row->height();
1949 pos_type const tmplast = rowLast(row);
1953 breakAgain(bview, row);
1954 if (row->height() == tmpheight && rowLast(row) == tmplast)
1955 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1957 status(bview, LyXText::NEED_MORE_REFRESH);
1959 // check the special right address boxes
1960 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1967 redoDrawingOfParagraph(bview, tmpcursor);
1970 // set the cursor again. Otherwise dangling pointers are possible
1971 // also set the selection
1973 if (selection.set()) {
1975 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1976 false, selection.cursor.boundary());
1977 selection.cursor = cursor;
1978 setCursorIntern(bview, selection.start.par(),
1979 selection.start.pos(),
1980 false, selection.start.boundary());
1981 selection.start = cursor;
1982 setCursorIntern(bview, selection.end.par(),
1983 selection.end.pos(),
1984 false, selection.end.boundary());
1985 selection.end = cursor;
1986 setCursorIntern(bview, last_sel_cursor.par(),
1987 last_sel_cursor.pos(),
1988 false, last_sel_cursor.boundary());
1989 last_sel_cursor = cursor;
1992 setCursorIntern(bview, cursor.par(), cursor.pos(),
1993 false, cursor.boundary());
1997 // returns false if inset wasn't found
1998 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2000 // first check the current paragraph
2001 int pos = cursor.par()->getPositionOfInset(inset);
2003 checkParagraph(bview, cursor.par(), pos);
2007 // check every paragraph
2009 Paragraph * par = ownerParagraph();
2011 pos = par->getPositionOfInset(inset);
2013 checkParagraph(bview, par, pos);
2023 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2025 bool setfont, bool boundary) const
2027 LyXCursor old_cursor = cursor;
2028 setCursorIntern(bview, par, pos, setfont, boundary);
2029 return deleteEmptyParagraphMechanism(bview, old_cursor);
2033 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2034 pos_type pos, bool boundary) const
2041 cur.boundary(boundary);
2043 // get the cursor y position in text
2045 Row * row = getRow(par, pos, y);
2046 Row * old_row = row;
2048 // if we are before the first char of this row and are still in the
2049 // same paragraph and there is a previous row then put the cursor on
2050 // the end of the previous row
2051 cur.iy(y + row->baseline());
2053 if (row->previous() && pos &&
2054 row->previous()->par() == row->par() &&
2055 par->getChar(pos) == Paragraph::META_INSET &&
2056 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2058 row = row->previous();
2063 // y is now the beginning of the cursor row
2064 y += row->baseline();
2065 // y is now the cursor baseline
2068 pos_type last = rowLastPrintable(old_row);
2070 if (pos > last + 1) {
2071 // This shouldn't happen.
2074 } else if (pos < row->pos()) {
2079 // now get the cursors x position
2080 float x = getCursorX(bview, row, pos, last, boundary);
2083 if (old_row != row) {
2084 x = getCursorX(bview, old_row, pos, last, boundary);
2091 float LyXText::getCursorX(BufferView * bview, Row * row,
2092 pos_type pos, pos_type last, bool boundary) const
2094 pos_type cursor_vpos = 0;
2096 float fill_separator;
2098 float fill_label_hfill;
2099 // This call HAS to be here because of the BidiTables!!!
2100 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2103 if (last < row->pos())
2104 cursor_vpos = row->pos();
2105 else if (pos > last && !boundary)
2106 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2107 ? row->pos() : last + 1;
2108 else if (pos > row->pos() &&
2109 (pos > last || boundary))
2110 /// Place cursor after char at (logical) position pos - 1
2111 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2112 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2114 /// Place cursor before char at (logical) position pos
2115 cursor_vpos = (bidi_level(pos) % 2 == 0)
2116 ? log2vis(pos) : log2vis(pos) + 1;
2118 pos_type main_body =
2119 beginningOfMainBody(bview->buffer(), row->par());
2120 if ((main_body > 0) &&
2121 ((main_body-1 > last) ||
2122 !row->par()->isLineSeparator(main_body-1)))
2125 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2126 pos_type pos = vis2log(vpos);
2127 if (main_body > 0 && pos == main_body - 1) {
2128 x += fill_label_hfill +
2129 font_metrics::width(
2130 row->par()->layout()->labelsep,
2131 getLabelFont(bview->buffer(),
2133 if (row->par()->isLineSeparator(main_body - 1))
2134 x -= singleWidth(bview,
2135 row->par(), main_body - 1);
2137 if (hfillExpansion(bview->buffer(), row, pos)) {
2138 x += singleWidth(bview, row->par(), pos);
2139 if (pos >= main_body)
2142 x += fill_label_hfill;
2143 } else if (row->par()->isSeparator(pos)) {
2144 x += singleWidth(bview, row->par(), pos);
2145 if (pos >= main_body)
2146 x += fill_separator;
2148 x += singleWidth(bview, row->par(), pos);
2154 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2155 pos_type pos, bool setfont, bool boundary) const
2157 InsetText * it = static_cast<InsetText *>(par->inInset());
2159 if (it != inset_owner) {
2160 lyxerr[Debug::INSETS] << "InsetText is " << it
2162 << "inset_owner is "
2163 << inset_owner << endl;
2164 #ifdef WITH_WARNINGS
2165 #warning I believe this code is wrong. (Lgb)
2166 #warning Jürgen, have a look at this. (Lgb)
2167 #warning Hmmm, I guess you are right but we
2168 #warning should verify when this is needed
2170 // Jürgen, would you like to have a look?
2171 // I guess we need to move the outer cursor
2172 // and open and lock the inset (bla bla bla)
2173 // stuff I don't know... so can you have a look?
2175 // I moved the lyxerr stuff in here so we can see if
2176 // this is actually really needed and where!
2178 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2183 setCursor(bview, cursor, par, pos, boundary);
2185 setCurrentFont(bview);
2189 void LyXText::setCurrentFont(BufferView * bview) const
2191 pos_type pos = cursor.pos();
2192 if (cursor.boundary() && pos > 0)
2196 if (pos == cursor.par()->size())
2198 else // potentional bug... BUG (Lgb)
2199 if (cursor.par()->isSeparator(pos)) {
2200 if (pos > cursor.row()->pos() &&
2201 bidi_level(pos) % 2 ==
2202 bidi_level(pos - 1) % 2)
2204 else if (pos + 1 < cursor.par()->size())
2210 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2211 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2213 if (cursor.pos() == cursor.par()->size() &&
2214 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2215 !cursor.boundary()) {
2216 Language const * lang =
2217 cursor.par()->getParLanguage(bview->buffer()->params);
2218 current_font.setLanguage(lang);
2219 current_font.setNumber(LyXFont::OFF);
2220 real_current_font.setLanguage(lang);
2221 real_current_font.setNumber(LyXFont::OFF);
2226 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2228 LyXCursor old_cursor = cursor;
2230 setCursorFromCoordinates(bview, cursor, x, y);
2231 setCurrentFont(bview);
2232 deleteEmptyParagraphMechanism(bview, old_cursor);
2236 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2239 // Get the row first.
2241 Row * row = getRowNearY(y);
2243 pos_type const column = getColumnNearX(bview, row, x, bound);
2244 cur.par(row->par());
2245 cur.pos(row->pos() + column);
2247 cur.y(y + row->baseline());
2250 if (row->next() && row->next()->pos() == cur.pos() &&
2251 cur.par() == row->next()->par() &&
2252 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2253 (ins=cur.par()->getInset(cur.pos())) &&
2254 (ins->needFullRow() || ins->display()))
2256 // we enter here if we put the cursor on the end of the row before
2257 // a inset which uses a full row and in that case we HAVE to calculate
2258 // the right (i) values.
2259 pos_type last = rowLastPrintable(row);
2260 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2262 cur.iy(y + row->height() + row->next()->baseline());
2263 cur.irow(row->next());
2269 cur.boundary(bound);
2273 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2275 if (cursor.pos() > 0) {
2276 bool boundary = cursor.boundary();
2277 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2278 if (!internal && !boundary &&
2279 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2280 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2281 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2282 Paragraph * par = cursor.par()->previous();
2283 setCursor(bview, par, par->size());
2288 void LyXText::cursorRight(BufferView * bview, bool internal) const
2290 if (!internal && cursor.boundary() &&
2291 !cursor.par()->isNewline(cursor.pos()))
2292 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2293 else if (cursor.pos() < cursor.par()->size()) {
2294 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2296 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2297 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2298 } else if (cursor.par()->next())
2299 setCursor(bview, cursor.par()->next(), 0);
2303 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2306 int x = cursor.x_fix();
2307 int y = cursor.y() - cursor.row()->baseline() - 1;
2308 setCursorFromCoordinates(bview, x, y);
2310 int y1 = cursor.iy() - first_y;
2314 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2315 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2316 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2320 setCursorFromCoordinates(bview, cursor.x_fix(),
2321 cursor.y() - cursor.row()->baseline() - 1);
2326 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2329 int x = cursor.x_fix();
2330 int y = cursor.y() - cursor.row()->baseline() +
2331 cursor.row()->height() + 1;
2332 setCursorFromCoordinates(bview, x, y);
2333 if (!selecting && cursor.row() == cursor.irow()) {
2334 int y1 = cursor.iy() - first_y;
2338 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2339 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2340 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2344 setCursorFromCoordinates(bview, cursor.x_fix(),
2345 cursor.y() - cursor.row()->baseline()
2346 + cursor.row()->height() + 1);
2351 void LyXText::cursorUpParagraph(BufferView * bview) const
2353 if (cursor.pos() > 0) {
2354 setCursor(bview, cursor.par(), 0);
2356 else if (cursor.par()->previous()) {
2357 setCursor(bview, cursor.par()->previous(), 0);
2362 void LyXText::cursorDownParagraph(BufferView * bview) const
2364 if (cursor.par()->next()) {
2365 setCursor(bview, cursor.par()->next(), 0);
2367 setCursor(bview, cursor.par(), cursor.par()->size());
2371 // fix the cursor `cur' after a characters has been deleted at `where'
2372 // position. Called by deleteEmptyParagraphMechanism
2373 void LyXText::fixCursorAfterDelete(BufferView * bview,
2375 LyXCursor const & where) const
2377 // if cursor is not in the paragraph where the delete occured,
2379 if (cur.par() != where.par())
2382 // if cursor position is after the place where the delete occured,
2384 if (cur.pos() > where.pos())
2385 cur.pos(cur.pos()-1);
2387 // check also if we don't want to set the cursor on a spot behind the
2388 // pagragraph because we erased the last character.
2389 if (cur.pos() > cur.par()->size())
2390 cur.pos(cur.par()->size());
2392 // recompute row et al. for this cursor
2393 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2397 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2398 LyXCursor const & old_cursor) const
2400 // Would be wrong to delete anything if we have a selection.
2401 if (selection.set())
2404 // We allow all kinds of "mumbo-jumbo" when freespacing.
2405 if (old_cursor.par()->layout()->free_spacing
2406 || old_cursor.par()->isFreeSpacing()) {
2410 /* Ok I'll put some comments here about what is missing.
2411 I have fixed BackSpace (and thus Delete) to not delete
2412 double-spaces automagically. I have also changed Cut,
2413 Copy and Paste to hopefully do some sensible things.
2414 There are still some small problems that can lead to
2415 double spaces stored in the document file or space at
2416 the beginning of paragraphs. This happens if you have
2417 the cursor betwenn to spaces and then save. Or if you
2418 cut and paste and the selection have a space at the
2419 beginning and then save right after the paste. I am
2420 sure none of these are very hard to fix, but I will
2421 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2422 that I can get some feedback. (Lgb)
2425 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2426 // delete the LineSeparator.
2429 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2430 // delete the LineSeparator.
2433 // If the pos around the old_cursor were spaces, delete one of them.
2434 if (old_cursor.par() != cursor.par()
2435 || old_cursor.pos() != cursor.pos()) {
2436 // Only if the cursor has really moved
2438 if (old_cursor.pos() > 0
2439 && old_cursor.pos() < old_cursor.par()->size()
2440 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2441 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2442 old_cursor.par()->erase(old_cursor.pos() - 1);
2443 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2445 #ifdef WITH_WARNINGS
2446 #warning This will not work anymore when we have multiple views of the same buffer
2447 // In this case, we will have to correct also the cursors held by
2448 // other bufferviews. It will probably be easier to do that in a more
2449 // automated way in LyXCursor code. (JMarc 26/09/2001)
2451 // correct all cursors held by the LyXText
2452 fixCursorAfterDelete(bview, cursor, old_cursor);
2453 fixCursorAfterDelete(bview, selection.cursor,
2455 fixCursorAfterDelete(bview, selection.start,
2457 fixCursorAfterDelete(bview, selection.end, old_cursor);
2458 fixCursorAfterDelete(bview, last_sel_cursor,
2460 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2461 fixCursorAfterDelete(bview, toggle_end_cursor,
2467 // don't delete anything if this is the ONLY paragraph!
2468 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2471 // Do not delete empty paragraphs with keepempty set.
2472 if (old_cursor.par()->layout()->keepempty)
2475 // only do our magic if we changed paragraph
2476 if (old_cursor.par() == cursor.par())
2479 // record if we have deleted a paragraph
2480 // we can't possibly have deleted a paragraph before this point
2481 bool deleted = false;
2483 if ((old_cursor.par()->size() == 0
2484 || (old_cursor.par()->size() == 1
2485 && old_cursor.par()->isLineSeparator(0)))) {
2486 // ok, we will delete anything
2487 LyXCursor tmpcursor;
2489 // make sure that you do not delete any environments
2490 status(bview, LyXText::NEED_MORE_REFRESH);
2493 if (old_cursor.row()->previous()) {
2494 refresh_row = old_cursor.row()->previous();
2495 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2497 cursor = old_cursor; // that undo can restore the right cursor position
2498 Paragraph * endpar = old_cursor.par()->next();
2499 if (endpar && endpar->getDepth()) {
2500 while (endpar && endpar->getDepth()) {
2501 endpar = endpar->next();
2504 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2508 removeRow(old_cursor.row());
2509 if (ownerParagraph() == old_cursor.par()) {
2510 ownerParagraph(ownerParagraph()->next());
2513 delete old_cursor.par();
2515 /* Breakagain the next par. Needed because of
2516 * the parindent that can occur or dissappear.
2517 * The next row can change its height, if
2518 * there is another layout before */
2519 if (refresh_row->next()) {
2520 breakAgain(bview, refresh_row->next());
2521 updateCounters(bview, refresh_row);
2523 setHeightOfRow(bview, refresh_row);
2525 refresh_row = old_cursor.row()->next();
2526 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2529 cursor = old_cursor; // that undo can restore the right cursor position
2530 Paragraph * endpar = old_cursor.par()->next();
2531 if (endpar && endpar->getDepth()) {
2532 while (endpar && endpar->getDepth()) {
2533 endpar = endpar->next();
2536 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2540 removeRow(old_cursor.row());
2542 if (ownerParagraph() == old_cursor.par()) {
2543 ownerParagraph(ownerParagraph()->next());
2546 delete old_cursor.par();
2548 /* Breakagain the next par. Needed because of
2549 the parindent that can occur or dissappear.
2550 The next row can change its height, if
2551 there is another layout before */
2553 breakAgain(bview, refresh_row);
2554 updateCounters(bview, refresh_row->previous());
2559 setCursorIntern(bview, cursor.par(), cursor.pos());
2561 if (selection.cursor.par() == old_cursor.par()
2562 && selection.cursor.pos() == old_cursor.pos()) {
2563 // correct selection
2564 selection.cursor = cursor;
2568 if (old_cursor.par()->stripLeadingSpaces()) {
2569 redoParagraphs(bview, old_cursor,
2570 old_cursor.par()->next());
2572 setCursorIntern(bview, cursor.par(), cursor.pos());
2573 selection.cursor = cursor;
2580 void LyXText::toggleAppendix(BufferView * bview)
2582 Paragraph * par = cursor.par();
2583 bool start = !par->params().startOfAppendix();
2585 // ensure that we have only one start_of_appendix in this document
2586 Paragraph * tmp = ownerParagraph();
2587 for (; tmp; tmp = tmp->next()) {
2588 tmp->params().startOfAppendix(false);
2591 par->params().startOfAppendix(start);
2593 // we can set the refreshing parameters now
2594 status(bview, LyXText::NEED_MORE_REFRESH);
2596 refresh_row = 0; // not needed for full update
2597 updateCounters(bview, 0);
2598 setCursor(bview, cursor.par(), cursor.pos());
2602 Paragraph * LyXText::ownerParagraph() const
2605 return inset_owner->paragraph();
2607 return bv_owner->buffer()->paragraph;
2611 void LyXText::ownerParagraph(Paragraph * p) const
2614 inset_owner->paragraph(p);
2616 bv_owner->buffer()->paragraph = p;
2621 void LyXText::ownerParagraph(int id, Paragraph * p) const
2623 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2624 if (op && op->inInset()) {
2625 static_cast<InsetText *>(op->inInset())->paragraph(p);
2632 LyXText::text_status LyXText::status() const
2638 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2640 // We should only go up with refreshing code so this means that if
2641 // we have a MORE refresh we should never set it to LITTLE if we still
2642 // didn't handle it (and then it will be UNCHANGED. Now as long as
2643 // we stay inside one LyXText this may work but we need to tell the
2644 // outermost LyXText that it should REALLY draw us if there is some
2645 // change in a Inset::LyXText. So you see that when we are inside a
2646 // inset's LyXText we give the LITTLE to the outermost LyXText to
2647 // tell'em that it should redraw the actual row (where the inset
2648 // resides! Capito?!
2650 if ((status_ != NEED_MORE_REFRESH)
2651 || (status_ == NEED_MORE_REFRESH
2652 && st != NEED_VERY_LITTLE_REFRESH))
2655 if (inset_owner && st != UNCHANGED) {
2656 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2657 if (!bview->text->refresh_row) {
2658 bview->text->refresh_row = bview->text->cursor.row();
2659 bview->text->refresh_y = bview->text->cursor.y() -
2660 bview->text->cursor.row()->baseline();