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(textclasslist[buf->params.textclass][
132 par->layout()].font);
134 tmpfont.realize(textclasslist.
135 Style(buf->params.textclass,
137 buf->params.language);
139 par_depth = par->getDepth();
143 #ifndef INHERIT_LANGUAGE
144 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
146 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
147 buf->params.language);
156 // Gets the fully instantiated font at a given position in a paragraph
157 // Basically the same routine as Paragraph::getFont() in paragraph.C.
158 // The difference is that this one is used for displaying, and thus we
159 // are allowed to make cosmetic improvements. For instance make footnotes
161 // If position is -1, we get the layout font of the paragraph.
162 // If position is -2, we get the font of the manual label of the paragraph.
163 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
166 lyx::Assert(pos >= 0);
168 LyXLayout const & layout =
169 textclasslist[buf->params.textclass][par->layout()];
171 Paragraph::depth_type par_depth = par->getDepth();
172 // We specialize the 95% common case:
174 if (layout.labeltype == LABEL_MANUAL
175 && pos < beginningOfMainBody(buf, par)) {
177 LyXFont f = par->getFontSettings(buf->params, pos);
179 par->inInset()->getDrawFont(f);
180 #ifndef INHERIT_LANGUAGE
181 return f.realize(layout.reslabelfont);
183 return f.realize(layout.reslabelfont, buf->params.language);
186 LyXFont f = par->getFontSettings(buf->params, pos);
188 par->inInset()->getDrawFont(f);
189 #ifndef INHERIT_LANGUAGE
190 return f.realize(layout.resfont);
192 return f.realize(layout.resfont, buf->params.language);
197 // The uncommon case need not be optimized as much
201 if (pos < beginningOfMainBody(buf, par)) {
203 layoutfont = layout.labelfont;
206 layoutfont = layout.font;
209 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
210 #ifndef INHERIT_LANGUAGE
211 tmpfont.realize(layoutfont);
213 tmpfont.realize(layoutfont, buf->params.language);
216 par->inInset()->getDrawFont(tmpfont);
218 return realizeFont(tmpfont, buf, par);
222 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
224 LyXLayout const & layout =
225 textclasslist[buf->params.textclass][par->layout()];
227 Paragraph::depth_type par_depth = par->getDepth();
230 return layout.resfont;
233 return realizeFont(layout.font, buf, par);
237 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
239 LyXLayout const & layout =
240 textclasslist[buf->params.textclass][par->layout()];
242 Paragraph::depth_type par_depth = par->getDepth();
245 return layout.reslabelfont;
248 return realizeFont(layout.labelfont, buf, par);
252 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
253 pos_type pos, LyXFont const & fnt,
256 Buffer const * buf = bv->buffer();
257 LyXFont font = getFont(buf, par, pos);
258 font.update(fnt, buf->params.language, toggleall);
259 // Let the insets convert their font
260 if (par->isInset(pos)) {
261 Inset * inset = par->getInset(pos);
262 if (isEditableInset(inset)) {
263 UpdatableInset * uinset =
264 static_cast<UpdatableInset *>(inset);
265 uinset->setFont(bv, fnt, toggleall, true);
269 // Plug thru to version below:
270 setCharFont(buf, par, pos, font);
274 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
275 pos_type pos, LyXFont const & fnt)
279 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
280 LyXLayout const & layout = tclass[par->layout()];
282 // Get concrete layout font to reduce against
285 if (pos < beginningOfMainBody(buf, par))
286 layoutfont = layout.labelfont;
288 layoutfont = layout.font;
290 // Realize against environment font information
291 if (par->getDepth()) {
292 Paragraph * tp = par;
293 while (!layoutfont.resolved() && tp && tp->getDepth()) {
294 tp = tp->outerHook();
296 #ifndef INHERIT_LANGUAGE
297 layoutfont.realize(tclass[tp->layout()].font);
299 layoutfont.realize(textclasslist.
300 Style(buf->params.textclass,
302 buf->params.language);
307 #ifndef INHERIT_LANGUAGE
308 layoutfont.realize(tclass.defaultfont());
310 layoutfont.realize(tclass.defaultfont(), buf->params.language);
313 // Now, reduce font against full layout font
314 font.reduce(layoutfont);
316 par->setFont(pos, font);
320 // inserts a new row behind the specified row, increments
321 // the touched counters
322 void LyXText::insertRow(Row * row, Paragraph * par,
325 Row * tmprow = new Row;
328 tmprow->next(firstrow);
331 tmprow->previous(row);
332 tmprow->next(row->next());
337 tmprow->next()->previous(tmprow);
339 if (tmprow->previous())
340 tmprow->previous()->next(tmprow);
352 // removes the row and reset the touched counters
353 void LyXText::removeRow(Row * row) const
355 Row * row_prev = row->previous();
357 row->next()->previous(row_prev);
359 firstrow = row->next();
360 // lyx::Assert(firstrow);
362 row_prev->next(row->next());
364 if (row == lastrow) {
365 lyx::Assert(!row->next());
368 if (refresh_row == row) {
369 refresh_row = row_prev ? row_prev : row->next();
370 // what about refresh_y, refresh_height
373 height -= row->height(); // the text becomes smaller
376 --number_of_rows; // one row less
380 // remove all following rows of the paragraph of the specified row.
381 void LyXText::removeParagraph(Row * row) const
383 Paragraph * tmppar = row->par();
387 while (row && row->par() == tmppar) {
388 tmprow = row->next();
395 // insert the specified paragraph behind the specified row
396 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
399 insertRow(row, par, 0); /* insert a new row, starting
402 setCounter(bview->buffer(), par); // set the counters
404 // and now append the whole paragraph behind the new row
407 appendParagraph(bview, firstrow);
409 row->next()->height(0);
410 appendParagraph(bview, row->next());
415 Inset * LyXText::getInset() const
418 if (cursor.pos() == 0 && cursor.par()->bibkey) {
419 inset = cursor.par()->bibkey;
420 } else if (cursor.pos() < cursor.par()->size()
421 && cursor.par()->isInset(cursor.pos())) {
422 inset = cursor.par()->getInset(cursor.pos());
428 void LyXText::toggleInset(BufferView * bview)
430 Inset * inset = getInset();
431 // is there an editable inset at cursor position?
432 if (!isEditableInset(inset)) {
433 // No, try to see if we are inside a collapsable inset
434 if (inset_owner && inset_owner->owner()
435 && inset_owner->owner()->isOpen()) {
436 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
437 inset_owner->owner()->close(bview);
441 //bview->owner()->message(inset->editMessage());
443 // do we want to keep this?? (JMarc)
444 if (!isHighlyEditableInset(inset))
445 setCursorParUndo(bview);
447 if (inset->isOpen()) {
453 inset->open(bview, !inset->isOpen());
458 /* used in setlayout */
459 // Asger is not sure we want to do this...
460 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
463 LyXLayout const & layout =
464 textclasslist[buf->params.textclass][par->layout()];
467 for (pos_type pos = 0; pos < par->size(); ++pos) {
468 if (pos < beginningOfMainBody(buf, par))
469 layoutfont = layout.labelfont;
471 layoutfont = layout.font;
473 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
474 tmpfont.reduce(layoutfont);
475 par->setFont(pos, tmpfont);
480 Paragraph * LyXText::setLayout(BufferView * bview,
481 LyXCursor & cur, LyXCursor & sstart_cur,
482 LyXCursor & send_cur,
483 string const & layout)
485 Paragraph * endpar = send_cur.par()->next();
486 Paragraph * undoendpar = endpar;
488 if (endpar && endpar->getDepth()) {
489 while (endpar && endpar->getDepth()) {
490 endpar = endpar->next();
494 endpar = endpar->next(); // because of parindents etc.
497 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
499 // ok we have a selection. This is always between sstart_cur
500 // and sel_end cursor
502 Paragraph * par = sstart_cur.par();
503 Paragraph * epar = send_cur.par()->next();
505 LyXLayout const & lyxlayout =
506 textclasslist[bview->buffer()->params.textclass][layout];
509 par->applyLayout(layout);
510 makeFontEntriesLayoutSpecific(bview->buffer(), par);
511 Paragraph * fppar = par;
512 fppar->params().spaceTop(lyxlayout.fill_top ?
513 VSpace(VSpace::VFILL)
514 : VSpace(VSpace::NONE));
515 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
516 VSpace(VSpace::VFILL)
517 : VSpace(VSpace::NONE));
518 if (lyxlayout.margintype == MARGIN_MANUAL)
519 par->setLabelWidthString(lyxlayout.labelstring());
520 if (lyxlayout.labeltype != LABEL_BIBLIO
522 delete fppar->bibkey;
527 } while (par != epar);
533 // set layout over selection and make a total rebreak of those paragraphs
534 void LyXText::setLayout(BufferView * bview, string const & layout)
536 LyXCursor tmpcursor = cursor; /* store the current cursor */
538 // if there is no selection just set the layout
539 // of the current paragraph */
540 if (!selection.set()) {
541 selection.start = cursor; // dummy selection
542 selection.end = cursor;
544 Paragraph * endpar = setLayout(bview, cursor, selection.start,
545 selection.end, layout);
546 redoParagraphs(bview, selection.start, endpar);
548 // we have to reset the selection, because the
549 // geometry could have changed
550 setCursor(bview, selection.start.par(),
551 selection.start.pos(), false);
552 selection.cursor = cursor;
553 setCursor(bview, selection.end.par(), selection.end.pos(), false);
554 updateCounters(bview, cursor.row());
557 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
561 // increment depth over selection and
562 // make a total rebreak of those paragraphs
563 void LyXText::incDepth(BufferView * bview)
565 // If there is no selection, just use the current paragraph
566 if (!selection.set()) {
567 selection.start = cursor; // dummy selection
568 selection.end = cursor;
571 // We end at the next paragraph with depth 0
572 Paragraph * endpar = selection.end.par()->next();
574 Paragraph * undoendpar = endpar;
576 if (endpar && endpar->getDepth()) {
577 while (endpar && endpar->getDepth()) {
578 endpar = endpar->next();
582 endpar = endpar->next(); // because of parindents etc.
585 setUndo(bview, Undo::EDIT,
586 selection.start.par(), undoendpar);
588 LyXCursor tmpcursor = cursor; // store the current cursor
590 // ok we have a selection. This is always between sel_start_cursor
591 // and sel_end cursor
592 cursor = selection.start;
594 bool anything_changed = false;
596 LyXTextClass const & tclass =
597 textclasslist[bview->buffer()->params.textclass];
600 // NOTE: you can't change the depth of a bibliography entry
601 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
602 Paragraph * prev = cursor.par()->previous();
605 if (cursor.par()->getDepth()
606 < prev->getMaxDepthAfter(bview->buffer())){
607 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
608 anything_changed = true;
612 if (cursor.par() == selection.end.par())
614 cursor.par(cursor.par()->next());
617 // if nothing changed set all depth to 0
618 if (!anything_changed) {
619 cursor = selection.start;
620 while (cursor.par() != selection.end.par()) {
621 cursor.par()->params().depth(0);
622 cursor.par(cursor.par()->next());
624 cursor.par()->params().depth(0);
627 redoParagraphs(bview, selection.start, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed
631 setCursor(bview, selection.start.par(), selection.start.pos());
632 selection.cursor = cursor;
633 setCursor(bview, selection.end.par(), selection.end.pos());
634 updateCounters(bview, cursor.row());
637 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
641 // decrement depth over selection and
642 // make a total rebreak of those paragraphs
643 void LyXText::decDepth(BufferView * bview)
645 // if there is no selection just set the layout
646 // of the current paragraph
647 if (!selection.set()) {
648 selection.start = cursor; // dummy selection
649 selection.end = cursor;
651 Paragraph * endpar = selection.end.par()->next();
652 Paragraph * undoendpar = endpar;
654 if (endpar && endpar->getDepth()) {
655 while (endpar && endpar->getDepth()) {
656 endpar = endpar->next();
660 endpar = endpar->next(); // because of parindents etc.
663 setUndo(bview, Undo::EDIT,
664 selection.start.par(), undoendpar);
666 LyXCursor tmpcursor = cursor; // store the current cursor
668 // ok we have a selection. This is always between sel_start_cursor
669 // and sel_end cursor
670 cursor = selection.start;
673 if (cursor.par()->params().depth()) {
674 cursor.par()->params()
675 .depth(cursor.par()->params().depth() - 1);
677 if (cursor.par() == selection.end.par()) {
680 cursor.par(cursor.par()->next());
683 redoParagraphs(bview, selection.start, endpar);
685 // we have to reset the selection, because the
686 // geometry could have changed
687 setCursor(bview, selection.start.par(),
688 selection.start.pos());
689 selection.cursor = cursor;
690 setCursor(bview, selection.end.par(), selection.end.pos());
691 updateCounters(bview, cursor.row());
694 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
698 // set font over selection and make a total rebreak of those paragraphs
699 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
701 // if there is no selection just set the current_font
702 if (!selection.set()) {
703 // Determine basis font
705 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
707 layoutfont = getLabelFont(bview->buffer(),
710 layoutfont = getLayoutFont(bview->buffer(),
713 // Update current font
714 real_current_font.update(font,
715 bview->buffer()->params.language,
718 // Reduce to implicit settings
719 current_font = real_current_font;
720 current_font.reduce(layoutfont);
721 // And resolve it completely
722 #ifndef INHERIT_LANGUAGE
723 real_current_font.realize(layoutfont);
725 real_current_font.realize(layoutfont,
726 bview->buffer()->params.language);
731 LyXCursor tmpcursor = cursor; // store the current cursor
733 // ok we have a selection. This is always between sel_start_cursor
734 // and sel_end cursor
736 setUndo(bview, Undo::EDIT,
737 selection.start.par(), selection.end.par()->next());
739 cursor = selection.start;
740 while (cursor.par() != selection.end.par() ||
741 cursor.pos() < selection.end.pos())
743 if (cursor.pos() < cursor.par()->size()) {
744 // an open footnote should behave like a closed one
745 setCharFont(bview, cursor.par(), cursor.pos(),
747 cursor.pos(cursor.pos() + 1);
750 cursor.par(cursor.par()->next());
755 redoParagraphs(bview, selection.start, selection.end.par()->next());
757 // we have to reset the selection, because the
758 // geometry could have changed, but we keep
759 // it for user convenience
760 setCursor(bview, selection.start.par(), selection.start.pos());
761 selection.cursor = cursor;
762 setCursor(bview, selection.end.par(), selection.end.pos());
764 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
765 tmpcursor.boundary());
769 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
771 Row * tmprow = cur.row();
772 int y = cur.y() - tmprow->baseline();
774 setHeightOfRow(bview, tmprow);
776 while (tmprow->previous()
777 && tmprow->previous()->par() == tmprow->par()) {
778 tmprow = tmprow->previous();
779 y -= tmprow->height();
780 setHeightOfRow(bview, tmprow);
783 // we can set the refreshing parameters now
784 status(bview, LyXText::NEED_MORE_REFRESH);
786 refresh_row = tmprow;
787 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
791 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
793 Row * tmprow = cur.row();
795 int y = cur.y() - tmprow->baseline();
796 setHeightOfRow(bview, tmprow);
798 while (tmprow->previous()
799 && tmprow->previous()->par() == tmprow->par()) {
800 tmprow = tmprow->previous();
801 y -= tmprow->height();
804 // we can set the refreshing parameters now
805 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
807 refresh_row = tmprow;
809 status(bview, LyXText::NEED_MORE_REFRESH);
810 setCursor(bview, cur.par(), cur.pos());
814 // deletes and inserts again all paragaphs between the cursor
815 // and the specified par
816 // This function is needed after SetLayout and SetFont etc.
817 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
818 Paragraph const * endpar) const
821 Paragraph * tmppar = 0;
822 Paragraph * first_phys_par = 0;
824 Row * tmprow = cur.row();
826 int y = cur.y() - tmprow->baseline();
828 if (!tmprow->previous()) {
829 // a trick/hack for UNDO
830 // This is needed because in an UNDO/REDO we could have changed
831 // the ownerParagrah() so the paragraph inside the row is NOT
832 // my really first par anymore. Got it Lars ;) (Jug 20011206)
833 first_phys_par = ownerParagraph();
835 first_phys_par = tmprow->par();
836 while (tmprow->previous()
837 && tmprow->previous()->par() == first_phys_par)
839 tmprow = tmprow->previous();
840 y -= tmprow->height();
844 // we can set the refreshing parameters now
845 status(bview, LyXText::NEED_MORE_REFRESH);
847 refresh_row = tmprow->previous(); /* the real refresh row will
848 be deleted, so I store
852 tmppar = tmprow->next()->par();
855 while (tmprow->next() && tmppar != endpar) {
856 removeRow(tmprow->next());
857 if (tmprow->next()) {
858 tmppar = tmprow->next()->par();
864 // remove the first one
865 tmprow2 = tmprow; /* this is because tmprow->previous()
867 tmprow = tmprow->previous();
870 tmppar = first_phys_par;
874 insertParagraph(bview, tmppar, tmprow);
878 while (tmprow->next()
879 && tmprow->next()->par() == tmppar) {
880 tmprow = tmprow->next();
882 tmppar = tmppar->next();
884 } while (tmppar && tmppar != endpar);
886 // this is because of layout changes
888 refresh_y -= refresh_row->height();
889 setHeightOfRow(bview, refresh_row);
891 refresh_row = firstrow;
893 setHeightOfRow(bview, refresh_row);
896 if (tmprow && tmprow->next())
897 setHeightOfRow(bview, tmprow->next());
901 void LyXText::fullRebreak(BufferView * bview)
907 if (need_break_row) {
908 breakAgain(bview, need_break_row);
915 // important for the screen
918 /* the cursor set functions have a special mechanism. When they
919 * realize, that you left an empty paragraph, they will delete it.
920 * They also delete the corresponding row */
922 // need the selection cursor:
923 void LyXText::setSelection(BufferView * bview)
925 bool const lsel = selection.set();
927 if (!selection.set()) {
928 last_sel_cursor = selection.cursor;
929 selection.start = selection.cursor;
930 selection.end = selection.cursor;
935 // first the toggling area
936 if (cursor.y() < last_sel_cursor.y()
937 || (cursor.y() == last_sel_cursor.y()
938 && cursor.x() < last_sel_cursor.x())) {
939 toggle_end_cursor = last_sel_cursor;
940 toggle_cursor = cursor;
942 toggle_end_cursor = cursor;
943 toggle_cursor = last_sel_cursor;
946 last_sel_cursor = cursor;
948 // and now the whole selection
950 if (selection.cursor.par() == cursor.par())
951 if (selection.cursor.pos() < cursor.pos()) {
952 selection.end = cursor;
953 selection.start = selection.cursor;
955 selection.end = selection.cursor;
956 selection.start = cursor;
958 else if (selection.cursor.y() < cursor.y() ||
959 (selection.cursor.y() == cursor.y()
960 && selection.cursor.x() < cursor.x())) {
961 selection.end = cursor;
962 selection.start = selection.cursor;
965 selection.end = selection.cursor;
966 selection.start = cursor;
969 // a selection with no contents is not a selection
970 if (selection.start.par() == selection.end.par() &&
971 selection.start.pos() == selection.end.pos())
972 selection.set(false);
974 if (inset_owner && (selection.set() || lsel))
975 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
979 string const LyXText::selectionAsString(Buffer const * buffer,
982 if (!selection.set()) return string();
985 // Special handling if the whole selection is within one paragraph
986 if (selection.start.par() == selection.end.par()) {
987 result += selection.start.par()->asString(buffer,
988 selection.start.pos(),
994 // The selection spans more than one paragraph
996 // First paragraph in selection
997 result += selection.start.par()->asString(buffer,
998 selection.start.pos(),
999 selection.start.par()->size(),
1003 // The paragraphs in between (if any)
1004 LyXCursor tmpcur(selection.start);
1005 tmpcur.par(tmpcur.par()->next());
1006 while (tmpcur.par() != selection.end.par()) {
1007 result += tmpcur.par()->asString(buffer, 0,
1008 tmpcur.par()->size(),
1010 tmpcur.par(tmpcur.par()->next());
1013 // Last paragraph in selection
1014 result += selection.end.par()->asString(buffer, 0,
1015 selection.end.pos(), label);
1021 void LyXText::clearSelection() const
1023 selection.set(false);
1024 selection.mark(false);
1025 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1026 // reset this in the bv_owner!
1027 if (bv_owner && bv_owner->text)
1028 bv_owner->text->xsel_cache.set(false);
1032 void LyXText::cursorHome(BufferView * bview) const
1034 setCursor(bview, cursor.par(), cursor.row()->pos());
1038 void LyXText::cursorEnd(BufferView * bview) const
1040 if (!cursor.row()->next()
1041 || cursor.row()->next()->par() != cursor.row()->par()) {
1042 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1044 if (cursor.par()->size() &&
1045 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1046 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1047 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1049 setCursor(bview,cursor.par(),
1050 rowLast(cursor.row()) + 1);
1056 void LyXText::cursorTop(BufferView * bview) const
1058 while (cursor.par()->previous())
1059 cursor.par(cursor.par()->previous());
1060 setCursor(bview, cursor.par(), 0);
1064 void LyXText::cursorBottom(BufferView * bview) const
1066 while (cursor.par()->next())
1067 cursor.par(cursor.par()->next());
1068 setCursor(bview, cursor.par(), cursor.par()->size());
1072 void LyXText::toggleFree(BufferView * bview,
1073 LyXFont const & font, bool toggleall)
1075 // If the mask is completely neutral, tell user
1076 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1077 // Could only happen with user style
1078 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1082 // Try implicit word selection
1083 // If there is a change in the language the implicit word selection
1085 LyXCursor resetCursor = cursor;
1086 bool implicitSelection = (font.language() == ignore_language
1087 && font.number() == LyXFont::IGNORE)
1088 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1091 setFont(bview, font, toggleall);
1093 // Implicit selections are cleared afterwards
1094 //and cursor is set to the original position.
1095 if (implicitSelection) {
1097 cursor = resetCursor;
1098 setCursor(bview, cursor.par(), cursor.pos());
1099 selection.cursor = cursor;
1102 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1106 string LyXText::getStringToIndex(BufferView * bview)
1110 // Try implicit word selection
1111 // If there is a change in the language the implicit word selection
1113 LyXCursor resetCursor = cursor;
1114 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1116 if (!selection.set()) {
1117 bview->owner()->message(_("Nothing to index!"));
1120 if (selection.start.par() != selection.end.par()) {
1121 bview->owner()->message(_("Cannot index more than one paragraph!"));
1125 idxstring = selectionAsString(bview->buffer(), false);
1127 // Implicit selections are cleared afterwards
1128 //and cursor is set to the original position.
1129 if (implicitSelection) {
1131 cursor = resetCursor;
1132 setCursor(bview, cursor.par(), cursor.pos());
1133 selection.cursor = cursor;
1139 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1140 Paragraph const * par) const
1142 if (textclasslist[buf->params.textclass][
1143 par->layout()].labeltype != LABEL_MANUAL)
1146 return par->beginningOfMainBody();
1150 /* the DTP switches for paragraphs. LyX will store them in the
1151 * first physicla paragraph. When a paragraph is broken, the top settings
1152 * rest, the bottom settings are given to the new one. So I can make shure,
1153 * they do not duplicate themself and you cannnot make dirty things with
1156 void LyXText::setParagraph(BufferView * bview,
1157 bool line_top, bool line_bottom,
1158 bool pagebreak_top, bool pagebreak_bottom,
1159 VSpace const & space_top,
1160 VSpace const & space_bottom,
1161 Spacing const & spacing,
1163 string labelwidthstring,
1166 LyXCursor tmpcursor = cursor;
1167 if (!selection.set()) {
1168 selection.start = cursor;
1169 selection.end = cursor;
1172 // make sure that the depth behind the selection are restored, too
1173 Paragraph * endpar = selection.end.par()->next();
1174 Paragraph * undoendpar = endpar;
1176 if (endpar && endpar->getDepth()) {
1177 while (endpar && endpar->getDepth()) {
1178 endpar = endpar->next();
1179 undoendpar = endpar;
1183 // because of parindents etc.
1184 endpar = endpar->next();
1187 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1190 Paragraph * tmppar = selection.end.par();
1191 LyXTextClass const & tclass =
1192 textclasslist[bview->buffer()->params.textclass];
1194 while (tmppar != selection.start.par()->previous()) {
1195 setCursor(bview, tmppar, 0);
1196 status(bview, LyXText::NEED_MORE_REFRESH);
1197 refresh_row = cursor.row();
1198 refresh_y = cursor.y() - cursor.row()->baseline();
1199 cursor.par()->params().lineTop(line_top);
1200 cursor.par()->params().lineBottom(line_bottom);
1201 cursor.par()->params().pagebreakTop(pagebreak_top);
1202 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1203 cursor.par()->params().spaceTop(space_top);
1204 cursor.par()->params().spaceBottom(space_bottom);
1205 cursor.par()->params().spacing(spacing);
1206 // does the layout allow the new alignment?
1207 LyXLayout const & layout = tclass[cursor.par()->layout()];
1209 if (align == LYX_ALIGN_LAYOUT)
1210 align = layout.align;
1211 if (align & layout.alignpossible) {
1212 if (align == layout.align)
1213 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1215 cursor.par()->params().align(align);
1217 cursor.par()->setLabelWidthString(labelwidthstring);
1218 cursor.par()->params().noindent(noindent);
1219 tmppar = cursor.par()->previous();
1222 redoParagraphs(bview, selection.start, endpar);
1225 setCursor(bview, selection.start.par(), selection.start.pos());
1226 selection.cursor = cursor;
1227 setCursor(bview, selection.end.par(), selection.end.pos());
1228 setSelection(bview);
1229 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1231 bview->updateInset(inset_owner, true);
1235 char loweralphaCounter(int n)
1237 if (n < 1 || n > 26)
1247 char alphaCounter(int n)
1249 if (n < 1 || n > 26)
1257 char hebrewCounter(int n)
1259 static const char hebrew[22] = {
1260 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1261 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1262 '÷', 'ø', 'ù', 'ú'
1264 if (n < 1 || n > 22)
1272 string const romanCounter(int n)
1274 static char const * roman[20] = {
1275 "i", "ii", "iii", "iv", "v",
1276 "vi", "vii", "viii", "ix", "x",
1277 "xi", "xii", "xiii", "xiv", "xv",
1278 "xvi", "xvii", "xviii", "xix", "xx"
1280 if (n < 1 || n > 20)
1289 // set the counter of a paragraph. This includes the labels
1290 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1292 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1293 LyXLayout const & layout = textclass[par->layout()];
1296 // copy the prev-counters to this one,
1297 // unless this is the first paragraph
1298 if (par->previous()) {
1299 for (int i = 0; i < 10; ++i) {
1300 par->setCounter(i, par->previous()->getFirstCounter(i));
1302 par->params().appendix(par->previous()->params().appendix());
1303 if (!par->params().appendix() && par->params().startOfAppendix()) {
1304 par->params().appendix(true);
1305 for (int i = 0; i < 10; ++i) {
1306 par->setCounter(i, 0);
1309 par->enumdepth = par->previous()->enumdepth;
1310 par->itemdepth = par->previous()->itemdepth;
1312 for (int i = 0; i < 10; ++i) {
1313 par->setCounter(i, 0);
1315 par->params().appendix(par->params().startOfAppendix());
1320 /* Maybe we have to increment the enumeration depth.
1321 * BUT, enumeration in a footnote is considered in isolation from its
1322 * surrounding paragraph so don't increment if this is the
1323 * first line of the footnote
1324 * AND, bibliographies can't have their depth changed ie. they
1325 * are always of depth 0
1328 && par->previous()->getDepth() < par->getDepth()
1329 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1330 && par->enumdepth < 3
1331 && layout.labeltype != LABEL_BIBLIO) {
1335 // Maybe we have to decrement the enumeration depth, see note above
1337 && par->previous()->getDepth() > par->getDepth()
1338 && layout.labeltype != LABEL_BIBLIO) {
1339 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1340 par->setCounter(6 + par->enumdepth,
1341 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1342 /* reset the counters.
1343 * A depth change is like a breaking layout
1345 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1346 par->setCounter(i, 0);
1349 if (!par->params().labelString().empty()) {
1350 par->params().labelString(string());
1353 if (layout.margintype == MARGIN_MANUAL) {
1354 if (par->params().labelWidthString().empty()) {
1355 par->setLabelWidthString(layout.labelstring());
1358 par->setLabelWidthString(string());
1361 // is it a layout that has an automatic label?
1362 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1364 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1365 if (i >= 0 && i<= buf->params.secnumdepth) {
1366 par->incCounter(i); // increment the counter
1368 // Is there a label? Useful for Chapter layout
1369 if (!par->params().appendix()) {
1370 if (!layout.labelstring().empty())
1371 par->params().labelString(layout.labelstring());
1373 par->params().labelString(string());
1375 if (!layout.labelstring_appendix().empty())
1376 par->params().labelString(layout.labelstring_appendix());
1378 par->params().labelString(string());
1383 if (!par->params().appendix()) {
1384 switch (2 * LABEL_COUNTER_CHAPTER -
1385 textclass.maxcounter() + i) {
1386 case LABEL_COUNTER_CHAPTER:
1387 s << par->getCounter(i);
1389 case LABEL_COUNTER_SECTION:
1390 s << par->getCounter(i - 1) << '.'
1391 << par->getCounter(i);
1393 case LABEL_COUNTER_SUBSECTION:
1394 s << par->getCounter(i - 2) << '.'
1395 << par->getCounter(i - 1) << '.'
1396 << par->getCounter(i);
1398 case LABEL_COUNTER_SUBSUBSECTION:
1399 s << par->getCounter(i - 3) << '.'
1400 << par->getCounter(i - 2) << '.'
1401 << par->getCounter(i - 1) << '.'
1402 << par->getCounter(i);
1405 case LABEL_COUNTER_PARAGRAPH:
1406 s << par->getCounter(i - 4) << '.'
1407 << par->getCounter(i - 3) << '.'
1408 << par->getCounter(i - 2) << '.'
1409 << par->getCounter(i - 1) << '.'
1410 << par->getCounter(i);
1412 case LABEL_COUNTER_SUBPARAGRAPH:
1413 s << par->getCounter(i - 5) << '.'
1414 << par->getCounter(i - 4) << '.'
1415 << par->getCounter(i - 3) << '.'
1416 << par->getCounter(i - 2) << '.'
1417 << par->getCounter(i - 1) << '.'
1418 << par->getCounter(i);
1422 // Can this ever be reached? And in the
1423 // case it is, how can this be correct?
1425 s << par->getCounter(i) << '.';
1428 } else { // appendix
1429 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1430 case LABEL_COUNTER_CHAPTER:
1431 if (par->isRightToLeftPar(buf->params))
1432 s << hebrewCounter(par->getCounter(i));
1434 s << alphaCounter(par->getCounter(i));
1436 case LABEL_COUNTER_SECTION:
1437 if (par->isRightToLeftPar(buf->params))
1438 s << hebrewCounter(par->getCounter(i - 1));
1440 s << alphaCounter(par->getCounter(i - 1));
1443 << par->getCounter(i);
1446 case LABEL_COUNTER_SUBSECTION:
1447 if (par->isRightToLeftPar(buf->params))
1448 s << hebrewCounter(par->getCounter(i - 2));
1450 s << alphaCounter(par->getCounter(i - 2));
1453 << par->getCounter(i-1) << '.'
1454 << par->getCounter(i);
1457 case LABEL_COUNTER_SUBSUBSECTION:
1458 if (par->isRightToLeftPar(buf->params))
1459 s << hebrewCounter(par->getCounter(i-3));
1461 s << alphaCounter(par->getCounter(i-3));
1464 << par->getCounter(i-2) << '.'
1465 << par->getCounter(i-1) << '.'
1466 << par->getCounter(i);
1469 case LABEL_COUNTER_PARAGRAPH:
1470 if (par->isRightToLeftPar(buf->params))
1471 s << hebrewCounter(par->getCounter(i-4));
1473 s << alphaCounter(par->getCounter(i-4));
1476 << par->getCounter(i-3) << '.'
1477 << par->getCounter(i-2) << '.'
1478 << par->getCounter(i-1) << '.'
1479 << par->getCounter(i);
1482 case LABEL_COUNTER_SUBPARAGRAPH:
1483 if (par->isRightToLeftPar(buf->params))
1484 s << hebrewCounter(par->getCounter(i-5));
1486 s << alphaCounter(par->getCounter(i-5));
1489 << par->getCounter(i-4) << '.'
1490 << par->getCounter(i-3) << '.'
1491 << par->getCounter(i-2) << '.'
1492 << par->getCounter(i-1) << '.'
1493 << par->getCounter(i);
1497 // Can this ever be reached? And in the
1498 // case it is, how can this be correct?
1500 s << par->getCounter(i) << '.';
1506 par->params().labelString(par->params().labelString() +s.str().c_str());
1507 // We really want to remove the c_str as soon as
1510 for (i++; i < 10; ++i) {
1511 // reset the following counters
1512 par->setCounter(i, 0);
1514 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1515 for (i++; i < 10; ++i) {
1516 // reset the following counters
1517 par->setCounter(i, 0);
1519 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1520 par->incCounter(i + par->enumdepth);
1521 int number = par->getCounter(i + par->enumdepth);
1525 switch (par->enumdepth) {
1527 if (par->isRightToLeftPar(buf->params))
1529 << hebrewCounter(number)
1533 << loweralphaCounter(number)
1537 if (par->isRightToLeftPar(buf->params))
1538 s << '.' << romanCounter(number);
1540 s << romanCounter(number) << '.';
1543 if (par->isRightToLeftPar(buf->params))
1545 << alphaCounter(number);
1547 s << alphaCounter(number)
1551 if (par->isRightToLeftPar(buf->params))
1558 par->params().labelString(s.str().c_str());
1560 for (i += par->enumdepth + 1; i < 10; ++i) {
1561 // reset the following counters
1562 par->setCounter(i, 0);
1566 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1567 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1569 int number = par->getCounter(i);
1571 InsetCommandParams p("bibitem" );
1572 par->bibkey = new InsetBibKey(p);
1574 par->bibkey->setCounter(number);
1575 par->params().labelString(layout.labelstring());
1577 // In biblio should't be following counters but...
1579 string s = layout.labelstring();
1581 // the caption hack:
1582 if (layout.labeltype == LABEL_SENSITIVE) {
1583 bool isOK (par->inInset() && par->inInset()->owner() &&
1584 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1587 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1589 = floatList.getType(tmp->type());
1590 // We should get the correct number here too.
1591 s = fl.name() + " #:";
1593 /* par->SetLayout(0);
1594 s = layout->labelstring; */
1595 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1596 ? " :úåòîùî øñç" : "Senseless: ";
1599 par->params().labelString(s);
1601 /* reset the enumeration counter. They are always resetted
1602 * when there is any other layout between */
1603 for (int i = 6 + par->enumdepth; i < 10; ++i)
1604 par->setCounter(i, 0);
1609 // Updates all counters BEHIND the row. Changed paragraphs
1610 // with a dynamic left margin will be rebroken.
1611 void LyXText::updateCounters(BufferView * bview, Row * row) const
1619 par = row->par()->next();
1623 while (row->par() != par)
1626 setCounter(bview->buffer(), par);
1628 // now check for the headline layouts. remember that they
1629 // have a dynamic left margin
1630 LyXTextClass const & tclass =
1631 textclasslist[bview->buffer()->params.textclass];
1632 LyXLayout const & layout = tclass[par->layout()];
1634 if (layout.margintype == MARGIN_DYNAMIC
1635 || layout.labeltype == LABEL_SENSITIVE) {
1636 // Rebreak the paragraph
1637 removeParagraph(row);
1638 appendParagraph(bview, row);
1645 void LyXText::insertInset(BufferView * bview, Inset * inset)
1647 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1649 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1651 cursor.par()->insertInset(cursor.pos(), inset);
1652 // Just to rebreak and refresh correctly.
1653 // The character will not be inserted a second time
1654 insertChar(bview, Paragraph::META_INSET);
1655 // If we enter a highly editable inset the cursor should be to before
1656 // the inset. This couldn't happen before as Undo was not handled inside
1657 // inset now after the Undo LyX tries to call inset->Edit(...) again
1658 // and cannot do this as the cursor is behind the inset and GetInset
1659 // does not return the inset!
1660 if (isHighlyEditableInset(inset)) {
1661 cursorLeft(bview, true);
1667 void LyXText::copyEnvironmentType()
1669 copylayouttype = cursor.par()->layout();
1673 void LyXText::pasteEnvironmentType(BufferView * bview)
1675 setLayout(bview, copylayouttype);
1679 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1681 // Stuff what we got on the clipboard. Even if there is no selection.
1683 // There is a problem with having the stuffing here in that the
1684 // larger the selection the slower LyX will get. This can be
1685 // solved by running the line below only when the selection has
1686 // finished. The solution used currently just works, to make it
1687 // faster we need to be more clever and probably also have more
1688 // calls to stuffClipboard. (Lgb)
1689 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1691 // This doesn't make sense, if there is no selection
1692 if (!selection.set())
1695 // OK, we have a selection. This is always between selection.start
1696 // and selection.end
1698 // make sure that the depth behind the selection are restored, too
1699 Paragraph * endpar = selection.end.par()->next();
1700 Paragraph * undoendpar = endpar;
1702 if (endpar && endpar->getDepth()) {
1703 while (endpar && endpar->getDepth()) {
1704 endpar = endpar->next();
1705 undoendpar = endpar;
1707 } else if (endpar) {
1708 endpar = endpar->next(); // because of parindents etc.
1711 setUndo(bview, Undo::DELETE,
1712 selection.start.par(), undoendpar);
1714 // there are two cases: cut only within one paragraph or
1715 // more than one paragraph
1716 if (selection.start.par() == selection.end.par()) {
1717 // only within one paragraph
1718 endpar = selection.end.par();
1719 int pos = selection.end.pos();
1720 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1721 selection.start.pos(), pos,
1722 bview->buffer()->params.textclass,
1724 selection.end.pos(pos);
1726 endpar = selection.end.par();
1727 int pos = selection.end.pos();
1728 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1729 selection.start.pos(), pos,
1730 bview->buffer()->params.textclass,
1733 selection.end.par(endpar);
1734 selection.end.pos(pos);
1735 cursor.pos(selection.end.pos());
1737 endpar = endpar->next();
1739 // sometimes necessary
1741 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1743 redoParagraphs(bview, selection.start, endpar);
1745 // cutSelection can invalidate the cursor so we need to set
1747 cursor = selection.start;
1749 // need a valid cursor. (Lgb)
1752 setCursor(bview, cursor.par(), cursor.pos());
1753 selection.cursor = cursor;
1754 updateCounters(bview, cursor.row());
1758 void LyXText::copySelection(BufferView * bview)
1760 // stuff the selection onto the X clipboard, from an explicit copy request
1761 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1763 // this doesnt make sense, if there is no selection
1764 if (!selection.set())
1767 // ok we have a selection. This is always between selection.start
1768 // and sel_end cursor
1770 // copy behind a space if there is one
1771 while (selection.start.par()->size() > selection.start.pos()
1772 && selection.start.par()->isLineSeparator(selection.start.pos())
1773 && (selection.start.par() != selection.end.par()
1774 || selection.start.pos() < selection.end.pos()))
1775 selection.start.pos(selection.start.pos() + 1);
1777 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1778 selection.start.pos(), selection.end.pos(),
1779 bview->buffer()->params.textclass);
1783 void LyXText::pasteSelection(BufferView * bview)
1785 // this does not make sense, if there is nothing to paste
1786 if (!CutAndPaste::checkPastePossible(cursor.par()))
1789 setUndo(bview, Undo::INSERT,
1790 cursor.par(), cursor.par()->next());
1793 Paragraph * actpar = cursor.par();
1794 int pos = cursor.pos();
1796 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1797 bview->buffer()->params.textclass);
1799 redoParagraphs(bview, cursor, endpar);
1801 setCursor(bview, cursor.par(), cursor.pos());
1804 selection.cursor = cursor;
1805 setCursor(bview, actpar, pos);
1806 setSelection(bview);
1807 updateCounters(bview, cursor.row());
1811 // sets the selection over the number of characters of string, no check!!
1812 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1817 selection.cursor = cursor;
1818 for (string::size_type i = 0; i < str.length(); ++i)
1820 setSelection(bview);
1824 // simple replacing. The font of the first selected character is used
1825 void LyXText::replaceSelectionWithString(BufferView * bview,
1828 setCursorParUndo(bview);
1831 if (!selection.set()) { // create a dummy selection
1832 selection.end = cursor;
1833 selection.start = cursor;
1836 // Get font setting before we cut
1837 pos_type pos = selection.end.pos();
1838 LyXFont const font = selection.start.par()
1839 ->getFontSettings(bview->buffer()->params,
1840 selection.start.pos());
1842 // Insert the new string
1843 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1844 selection.end.par()->insertChar(pos, (*cit), font);
1848 // Cut the selection
1849 cutSelection(bview, true, false);
1855 // needed to insert the selection
1856 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1858 Paragraph * par = cursor.par();
1859 pos_type pos = cursor.pos();
1860 Paragraph * endpar = cursor.par()->next();
1862 setCursorParUndo(bview);
1864 // only to be sure, should not be neccessary
1867 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1869 redoParagraphs(bview, cursor, endpar);
1870 setCursor(bview, cursor.par(), cursor.pos());
1871 selection.cursor = cursor;
1872 setCursor(bview, par, pos);
1873 setSelection(bview);
1877 // turns double-CR to single CR, others where converted into one
1878 // blank. Then InsertStringAsLines is called
1879 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1881 string linestr(str);
1882 bool newline_inserted = false;
1883 for (string::size_type i = 0; i < linestr.length(); ++i) {
1884 if (linestr[i] == '\n') {
1885 if (newline_inserted) {
1886 // we know that \r will be ignored by
1887 // InsertStringA. Of course, it is a dirty
1888 // trick, but it works...
1889 linestr[i - 1] = '\r';
1893 newline_inserted = true;
1895 } else if (IsPrintable(linestr[i])) {
1896 newline_inserted = false;
1899 insertStringAsLines(bview, linestr);
1903 bool LyXText::gotoNextInset(BufferView * bview,
1904 vector<Inset::Code> const & codes,
1905 string const & contents) const
1907 LyXCursor res = cursor;
1910 if (res.pos() < res.par()->size() - 1) {
1911 res.pos(res.pos() + 1);
1913 res.par(res.par()->next());
1917 } while (res.par() &&
1918 !(res.par()->isInset(res.pos())
1919 && (inset = res.par()->getInset(res.pos())) != 0
1920 && find(codes.begin(), codes.end(), inset->lyxCode())
1922 && (contents.empty() ||
1923 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1927 setCursor(bview, res.par(), res.pos(), false);
1934 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1937 LyXCursor tmpcursor;
1941 Row * row = getRow(par, pos, y);
1943 // is there a break one row above
1944 if (row->previous() && row->previous()->par() == row->par()) {
1945 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1946 if (z >= row->pos()) {
1947 // set the dimensions of the row above
1948 y -= row->previous()->height();
1950 refresh_row = row->previous();
1951 status(bview, LyXText::NEED_MORE_REFRESH);
1953 breakAgain(bview, row->previous());
1955 // set the cursor again. Otherwise
1956 // dangling pointers are possible
1957 setCursor(bview, cursor.par(), cursor.pos(),
1958 false, cursor.boundary());
1959 selection.cursor = cursor;
1964 int const tmpheight = row->height();
1965 pos_type const tmplast = rowLast(row);
1969 breakAgain(bview, row);
1970 if (row->height() == tmpheight && rowLast(row) == tmplast)
1971 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1973 status(bview, LyXText::NEED_MORE_REFRESH);
1975 // check the special right address boxes
1976 if (textclasslist[bview->buffer()->params.textclass][
1977 par->layout()].margintype
1978 == MARGIN_RIGHT_ADDRESS_BOX)
1986 redoDrawingOfParagraph(bview, tmpcursor);
1989 // set the cursor again. Otherwise dangling pointers are possible
1990 // also set the selection
1992 if (selection.set()) {
1994 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1995 false, selection.cursor.boundary());
1996 selection.cursor = cursor;
1997 setCursorIntern(bview, selection.start.par(),
1998 selection.start.pos(),
1999 false, selection.start.boundary());
2000 selection.start = cursor;
2001 setCursorIntern(bview, selection.end.par(),
2002 selection.end.pos(),
2003 false, selection.end.boundary());
2004 selection.end = cursor;
2005 setCursorIntern(bview, last_sel_cursor.par(),
2006 last_sel_cursor.pos(),
2007 false, last_sel_cursor.boundary());
2008 last_sel_cursor = cursor;
2011 setCursorIntern(bview, cursor.par(), cursor.pos(),
2012 false, cursor.boundary());
2016 // returns false if inset wasn't found
2017 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2019 // first check the current paragraph
2020 int pos = cursor.par()->getPositionOfInset(inset);
2022 checkParagraph(bview, cursor.par(), pos);
2026 // check every paragraph
2028 Paragraph * par = ownerParagraph();
2030 pos = par->getPositionOfInset(inset);
2032 checkParagraph(bview, par, pos);
2042 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2044 bool setfont, bool boundary) const
2046 LyXCursor old_cursor = cursor;
2047 setCursorIntern(bview, par, pos, setfont, boundary);
2048 return deleteEmptyParagraphMechanism(bview, old_cursor);
2052 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2053 pos_type pos, bool boundary) const
2060 cur.boundary(boundary);
2062 // get the cursor y position in text
2064 Row * row = getRow(par, pos, y);
2065 Row * old_row = row;
2067 // if we are before the first char of this row and are still in the
2068 // same paragraph and there is a previous row then put the cursor on
2069 // the end of the previous row
2070 cur.iy(y + row->baseline());
2072 if (row->previous() && pos &&
2073 row->previous()->par() == row->par() &&
2074 par->getChar(pos) == Paragraph::META_INSET &&
2075 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2077 row = row->previous();
2082 // y is now the beginning of the cursor row
2083 y += row->baseline();
2084 // y is now the cursor baseline
2087 pos_type last = rowLastPrintable(old_row);
2089 if (pos > last + 1) {
2090 // This shouldn't happen.
2093 } else if (pos < row->pos()) {
2098 // now get the cursors x position
2099 float x = getCursorX(bview, row, pos, last, boundary);
2102 if (old_row != row) {
2103 x = getCursorX(bview, old_row, pos, last, boundary);
2110 float LyXText::getCursorX(BufferView * bview, Row * row,
2111 pos_type pos, pos_type last, bool boundary) const
2113 pos_type cursor_vpos = 0;
2115 float fill_separator;
2117 float fill_label_hfill;
2118 // This call HAS to be here because of the BidiTables!!!
2119 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2122 if (last < row->pos())
2123 cursor_vpos = row->pos();
2124 else if (pos > last && !boundary)
2125 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2126 ? row->pos() : last + 1;
2127 else if (pos > row->pos() &&
2128 (pos > last || boundary))
2129 /// Place cursor after char at (logical) position pos - 1
2130 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2131 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2133 /// Place cursor before char at (logical) position pos
2134 cursor_vpos = (bidi_level(pos) % 2 == 0)
2135 ? log2vis(pos) : log2vis(pos) + 1;
2137 pos_type main_body =
2138 beginningOfMainBody(bview->buffer(), row->par());
2139 if ((main_body > 0) &&
2140 ((main_body-1 > last) ||
2141 !row->par()->isLineSeparator(main_body-1)))
2144 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2145 pos_type pos = vis2log(vpos);
2146 if (main_body > 0 && pos == main_body - 1) {
2147 x += fill_label_hfill +
2148 font_metrics::width(textclasslist[
2149 bview->buffer()->params.textclass][
2150 row->par()->layout()]
2152 getLabelFont(bview->buffer(), row->par()));
2153 if (row->par()->isLineSeparator(main_body-1))
2154 x -= singleWidth(bview, row->par(),main_body-1);
2156 if (hfillExpansion(bview->buffer(), row, pos)) {
2157 x += singleWidth(bview, row->par(), pos);
2158 if (pos >= main_body)
2161 x += fill_label_hfill;
2162 } else if (row->par()->isSeparator(pos)) {
2163 x += singleWidth(bview, row->par(), pos);
2164 if (pos >= main_body)
2165 x += fill_separator;
2167 x += singleWidth(bview, row->par(), pos);
2173 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2174 pos_type pos, bool setfont, bool boundary) const
2176 InsetText * it = static_cast<InsetText *>(par->inInset());
2178 if (it != inset_owner) {
2179 lyxerr[Debug::INSETS] << "InsetText is " << it
2181 << "inset_owner is "
2182 << inset_owner << endl;
2183 #ifdef WITH_WARNINGS
2184 #warning I believe this code is wrong. (Lgb)
2185 #warning Jürgen, have a look at this. (Lgb)
2186 #warning Hmmm, I guess you are right but we
2187 #warning should verify when this is needed
2189 // Jürgen, would you like to have a look?
2190 // I guess we need to move the outer cursor
2191 // and open and lock the inset (bla bla bla)
2192 // stuff I don't know... so can you have a look?
2194 // I moved the lyxerr stuff in here so we can see if
2195 // this is actually really needed and where!
2197 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2202 setCursor(bview, cursor, par, pos, boundary);
2204 setCurrentFont(bview);
2208 void LyXText::setCurrentFont(BufferView * bview) const
2210 pos_type pos = cursor.pos();
2211 if (cursor.boundary() && pos > 0)
2215 if (pos == cursor.par()->size())
2217 else // potentional bug... BUG (Lgb)
2218 if (cursor.par()->isSeparator(pos)) {
2219 if (pos > cursor.row()->pos() &&
2220 bidi_level(pos) % 2 ==
2221 bidi_level(pos - 1) % 2)
2223 else if (pos + 1 < cursor.par()->size())
2229 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2230 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2232 if (cursor.pos() == cursor.par()->size() &&
2233 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2234 !cursor.boundary()) {
2235 Language const * lang =
2236 cursor.par()->getParLanguage(bview->buffer()->params);
2237 current_font.setLanguage(lang);
2238 current_font.setNumber(LyXFont::OFF);
2239 real_current_font.setLanguage(lang);
2240 real_current_font.setNumber(LyXFont::OFF);
2245 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2247 LyXCursor old_cursor = cursor;
2249 setCursorFromCoordinates(bview, cursor, x, y);
2250 setCurrentFont(bview);
2251 deleteEmptyParagraphMechanism(bview, old_cursor);
2255 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2258 // Get the row first.
2260 Row * row = getRowNearY(y);
2262 pos_type const column = getColumnNearX(bview, row, x, bound);
2263 cur.par(row->par());
2264 cur.pos(row->pos() + column);
2266 cur.y(y + row->baseline());
2269 if (row->next() && row->next()->pos() == cur.pos() &&
2270 cur.par() == row->next()->par() &&
2271 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2272 (ins=cur.par()->getInset(cur.pos())) &&
2273 (ins->needFullRow() || ins->display()))
2275 // we enter here if we put the cursor on the end of the row before
2276 // a inset which uses a full row and in that case we HAVE to calculate
2277 // the right (i) values.
2278 pos_type last = rowLastPrintable(row);
2279 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2281 cur.iy(y + row->height() + row->next()->baseline());
2282 cur.irow(row->next());
2288 cur.boundary(bound);
2292 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2294 if (cursor.pos() > 0) {
2295 bool boundary = cursor.boundary();
2296 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2297 if (!internal && !boundary &&
2298 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2299 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2300 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2301 Paragraph * par = cursor.par()->previous();
2302 setCursor(bview, par, par->size());
2307 void LyXText::cursorRight(BufferView * bview, bool internal) const
2309 if (!internal && cursor.boundary() &&
2310 !cursor.par()->isNewline(cursor.pos()))
2311 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2312 else if (cursor.pos() < cursor.par()->size()) {
2313 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2315 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2316 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2317 } else if (cursor.par()->next())
2318 setCursor(bview, cursor.par()->next(), 0);
2322 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2325 int x = cursor.x_fix();
2326 int y = cursor.y() - cursor.row()->baseline() - 1;
2327 setCursorFromCoordinates(bview, x, y);
2329 int y1 = cursor.iy() - first_y;
2333 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2334 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2335 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2339 setCursorFromCoordinates(bview, cursor.x_fix(),
2340 cursor.y() - cursor.row()->baseline() - 1);
2345 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2348 int x = cursor.x_fix();
2349 int y = cursor.y() - cursor.row()->baseline() +
2350 cursor.row()->height() + 1;
2351 setCursorFromCoordinates(bview, x, y);
2352 if (!selecting && cursor.row() == cursor.irow()) {
2353 int y1 = cursor.iy() - first_y;
2357 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2358 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2359 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2363 setCursorFromCoordinates(bview, cursor.x_fix(),
2364 cursor.y() - cursor.row()->baseline()
2365 + cursor.row()->height() + 1);
2370 void LyXText::cursorUpParagraph(BufferView * bview) const
2372 if (cursor.pos() > 0) {
2373 setCursor(bview, cursor.par(), 0);
2375 else if (cursor.par()->previous()) {
2376 setCursor(bview, cursor.par()->previous(), 0);
2381 void LyXText::cursorDownParagraph(BufferView * bview) const
2383 if (cursor.par()->next()) {
2384 setCursor(bview, cursor.par()->next(), 0);
2386 setCursor(bview, cursor.par(), cursor.par()->size());
2390 // fix the cursor `cur' after a characters has been deleted at `where'
2391 // position. Called by deleteEmptyParagraphMechanism
2392 void LyXText::fixCursorAfterDelete(BufferView * bview,
2394 LyXCursor const & where) const
2396 // if cursor is not in the paragraph where the delete occured,
2398 if (cur.par() != where.par())
2401 // if cursor position is after the place where the delete occured,
2403 if (cur.pos() > where.pos())
2404 cur.pos(cur.pos()-1);
2406 // check also if we don't want to set the cursor on a spot behind the
2407 // pagragraph because we erased the last character.
2408 if (cur.pos() > cur.par()->size())
2409 cur.pos(cur.par()->size());
2411 // recompute row et al. for this cursor
2412 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2416 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2417 LyXCursor const & old_cursor) const
2419 // Would be wrong to delete anything if we have a selection.
2420 if (selection.set())
2423 // We allow all kinds of "mumbo-jumbo" when freespacing.
2424 if (textclasslist[bview->buffer()->params.textclass][
2425 old_cursor.par()->layout()].free_spacing
2426 || old_cursor.par()->isFreeSpacing())
2431 /* Ok I'll put some comments here about what is missing.
2432 I have fixed BackSpace (and thus Delete) to not delete
2433 double-spaces automagically. I have also changed Cut,
2434 Copy and Paste to hopefully do some sensible things.
2435 There are still some small problems that can lead to
2436 double spaces stored in the document file or space at
2437 the beginning of paragraphs. This happens if you have
2438 the cursor betwenn to spaces and then save. Or if you
2439 cut and paste and the selection have a space at the
2440 beginning and then save right after the paste. I am
2441 sure none of these are very hard to fix, but I will
2442 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2443 that I can get some feedback. (Lgb)
2446 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2447 // delete the LineSeparator.
2450 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2451 // delete the LineSeparator.
2454 // If the pos around the old_cursor were spaces, delete one of them.
2455 if (old_cursor.par() != cursor.par()
2456 || old_cursor.pos() != cursor.pos()) {
2457 // Only if the cursor has really moved
2459 if (old_cursor.pos() > 0
2460 && old_cursor.pos() < old_cursor.par()->size()
2461 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2462 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2463 old_cursor.par()->erase(old_cursor.pos() - 1);
2464 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2466 #ifdef WITH_WARNINGS
2467 #warning This will not work anymore when we have multiple views of the same buffer
2468 // In this case, we will have to correct also the cursors held by
2469 // other bufferviews. It will probably be easier to do that in a more
2470 // automated way in LyXCursor code. (JMarc 26/09/2001)
2472 // correct all cursors held by the LyXText
2473 fixCursorAfterDelete(bview, cursor, old_cursor);
2474 fixCursorAfterDelete(bview, selection.cursor,
2476 fixCursorAfterDelete(bview, selection.start,
2478 fixCursorAfterDelete(bview, selection.end, old_cursor);
2479 fixCursorAfterDelete(bview, last_sel_cursor,
2481 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2482 fixCursorAfterDelete(bview, toggle_end_cursor,
2488 // don't delete anything if this is the ONLY paragraph!
2489 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2492 // Do not delete empty paragraphs with keepempty set.
2494 [bview->buffer()->params.textclass]
2495 [old_cursor.par()->layout()].keepempty)
2498 // only do our magic if we changed paragraph
2499 if (old_cursor.par() == cursor.par())
2502 // record if we have deleted a paragraph
2503 // we can't possibly have deleted a paragraph before this point
2504 bool deleted = false;
2506 if ((old_cursor.par()->size() == 0
2507 || (old_cursor.par()->size() == 1
2508 && old_cursor.par()->isLineSeparator(0)))) {
2509 // ok, we will delete anything
2510 LyXCursor tmpcursor;
2512 // make sure that you do not delete any environments
2513 status(bview, LyXText::NEED_MORE_REFRESH);
2516 if (old_cursor.row()->previous()) {
2517 refresh_row = old_cursor.row()->previous();
2518 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2520 cursor = old_cursor; // that undo can restore the right cursor position
2521 Paragraph * endpar = old_cursor.par()->next();
2522 if (endpar && endpar->getDepth()) {
2523 while (endpar && endpar->getDepth()) {
2524 endpar = endpar->next();
2527 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2531 removeRow(old_cursor.row());
2532 if (ownerParagraph() == old_cursor.par()) {
2533 ownerParagraph(ownerParagraph()->next());
2536 delete old_cursor.par();
2538 /* Breakagain the next par. Needed because of
2539 * the parindent that can occur or dissappear.
2540 * The next row can change its height, if
2541 * there is another layout before */
2542 if (refresh_row->next()) {
2543 breakAgain(bview, refresh_row->next());
2544 updateCounters(bview, refresh_row);
2546 setHeightOfRow(bview, refresh_row);
2548 refresh_row = old_cursor.row()->next();
2549 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2552 cursor = old_cursor; // that undo can restore the right cursor position
2553 Paragraph * endpar = old_cursor.par()->next();
2554 if (endpar && endpar->getDepth()) {
2555 while (endpar && endpar->getDepth()) {
2556 endpar = endpar->next();
2559 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2563 removeRow(old_cursor.row());
2565 if (ownerParagraph() == old_cursor.par()) {
2566 ownerParagraph(ownerParagraph()->next());
2569 delete old_cursor.par();
2571 /* Breakagain the next par. Needed because of
2572 the parindent that can occur or dissappear.
2573 The next row can change its height, if
2574 there is another layout before */
2576 breakAgain(bview, refresh_row);
2577 updateCounters(bview, refresh_row->previous());
2582 setCursorIntern(bview, cursor.par(), cursor.pos());
2584 if (selection.cursor.par() == old_cursor.par()
2585 && selection.cursor.pos() == old_cursor.pos()) {
2586 // correct selection
2587 selection.cursor = cursor;
2591 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2592 redoParagraphs(bview, old_cursor,
2593 old_cursor.par()->next());
2595 setCursorIntern(bview, cursor.par(), cursor.pos());
2596 selection.cursor = cursor;
2603 void LyXText::toggleAppendix(BufferView * bview)
2605 Paragraph * par = cursor.par();
2606 bool start = !par->params().startOfAppendix();
2608 // ensure that we have only one start_of_appendix in this document
2609 Paragraph * tmp = ownerParagraph();
2610 for (; tmp; tmp = tmp->next()) {
2611 tmp->params().startOfAppendix(false);
2614 par->params().startOfAppendix(start);
2616 // we can set the refreshing parameters now
2617 status(bview, LyXText::NEED_MORE_REFRESH);
2619 refresh_row = 0; // not needed for full update
2620 updateCounters(bview, 0);
2621 setCursor(bview, cursor.par(), cursor.pos());
2625 Paragraph * LyXText::ownerParagraph() const
2628 return inset_owner->paragraph();
2630 return bv_owner->buffer()->paragraph;
2634 void LyXText::ownerParagraph(Paragraph * p) const
2637 inset_owner->paragraph(p);
2639 bv_owner->buffer()->paragraph = p;
2644 void LyXText::ownerParagraph(int id, Paragraph * p) const
2646 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2647 if (op && op->inInset()) {
2648 static_cast<InsetText *>(op->inInset())->paragraph(p);
2655 LyXText::text_status LyXText::status() const
2661 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2663 // We should only go up with refreshing code so this means that if
2664 // we have a MORE refresh we should never set it to LITTLE if we still
2665 // didn't handle it (and then it will be UNCHANGED. Now as long as
2666 // we stay inside one LyXText this may work but we need to tell the
2667 // outermost LyXText that it should REALLY draw us if there is some
2668 // change in a Inset::LyXText. So you see that when we are inside a
2669 // inset's LyXText we give the LITTLE to the outermost LyXText to
2670 // tell'em that it should redraw the actual row (where the inset
2671 // resides! Capito?!
2673 if ((status_ != NEED_MORE_REFRESH)
2674 || (status_ == NEED_MORE_REFRESH
2675 && st != NEED_VERY_LITTLE_REFRESH))
2678 if (inset_owner && st != UNCHANGED) {
2679 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2680 if (!bview->text->refresh_row) {
2681 bview->text->refresh_row = bview->text->cursor.row();
2682 bview->text->refresh_y = bview->text->cursor.y() -
2683 bview->text->cursor.row()->baseline();