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 first
1151 // physicla paragraph. When a paragraph is broken, the top settings rest,
1152 // 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.A depth change is like a breaking layout
1343 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1344 par->setCounter(i, 0);
1347 if (!par->params().labelString().empty()) {
1348 par->params().labelString(string());
1351 if (layout.margintype == MARGIN_MANUAL) {
1352 if (par->params().labelWidthString().empty()) {
1353 par->setLabelWidthString(layout.labelstring());
1356 par->setLabelWidthString(string());
1359 // is it a layout that has an automatic label?
1360 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1362 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1363 if (i >= 0 && i<= buf->params.secnumdepth) {
1364 par->incCounter(i); // increment the counter
1366 // Is there a label? Useful for Chapter layout
1367 if (!par->params().appendix()) {
1368 if (!layout.labelstring().empty())
1369 par->params().labelString(layout.labelstring());
1371 par->params().labelString(string());
1373 if (!layout.labelstring_appendix().empty())
1374 par->params().labelString(layout.labelstring_appendix());
1376 par->params().labelString(string());
1381 if (!par->params().appendix()) {
1382 switch (2 * LABEL_COUNTER_CHAPTER -
1383 textclass.maxcounter() + i) {
1384 case LABEL_COUNTER_CHAPTER:
1385 s << par->getCounter(i);
1387 case LABEL_COUNTER_SECTION:
1388 s << par->getCounter(i - 1) << '.'
1389 << par->getCounter(i);
1391 case LABEL_COUNTER_SUBSECTION:
1392 s << par->getCounter(i - 2) << '.'
1393 << par->getCounter(i - 1) << '.'
1394 << par->getCounter(i);
1396 case LABEL_COUNTER_SUBSUBSECTION:
1397 s << par->getCounter(i - 3) << '.'
1398 << par->getCounter(i - 2) << '.'
1399 << par->getCounter(i - 1) << '.'
1400 << par->getCounter(i);
1403 case LABEL_COUNTER_PARAGRAPH:
1404 s << par->getCounter(i - 4) << '.'
1405 << par->getCounter(i - 3) << '.'
1406 << par->getCounter(i - 2) << '.'
1407 << par->getCounter(i - 1) << '.'
1408 << par->getCounter(i);
1410 case LABEL_COUNTER_SUBPARAGRAPH:
1411 s << par->getCounter(i - 5) << '.'
1412 << par->getCounter(i - 4) << '.'
1413 << par->getCounter(i - 3) << '.'
1414 << par->getCounter(i - 2) << '.'
1415 << par->getCounter(i - 1) << '.'
1416 << par->getCounter(i);
1420 // Can this ever be reached? And in the
1421 // case it is, how can this be correct?
1423 s << par->getCounter(i) << '.';
1426 } else { // appendix
1427 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1428 case LABEL_COUNTER_CHAPTER:
1429 if (par->isRightToLeftPar(buf->params))
1430 s << hebrewCounter(par->getCounter(i));
1432 s << alphaCounter(par->getCounter(i));
1434 case LABEL_COUNTER_SECTION:
1435 if (par->isRightToLeftPar(buf->params))
1436 s << hebrewCounter(par->getCounter(i - 1));
1438 s << alphaCounter(par->getCounter(i - 1));
1441 << par->getCounter(i);
1444 case LABEL_COUNTER_SUBSECTION:
1445 if (par->isRightToLeftPar(buf->params))
1446 s << hebrewCounter(par->getCounter(i - 2));
1448 s << alphaCounter(par->getCounter(i - 2));
1451 << par->getCounter(i-1) << '.'
1452 << par->getCounter(i);
1455 case LABEL_COUNTER_SUBSUBSECTION:
1456 if (par->isRightToLeftPar(buf->params))
1457 s << hebrewCounter(par->getCounter(i-3));
1459 s << alphaCounter(par->getCounter(i-3));
1462 << par->getCounter(i-2) << '.'
1463 << par->getCounter(i-1) << '.'
1464 << par->getCounter(i);
1467 case LABEL_COUNTER_PARAGRAPH:
1468 if (par->isRightToLeftPar(buf->params))
1469 s << hebrewCounter(par->getCounter(i-4));
1471 s << alphaCounter(par->getCounter(i-4));
1474 << par->getCounter(i-3) << '.'
1475 << par->getCounter(i-2) << '.'
1476 << par->getCounter(i-1) << '.'
1477 << par->getCounter(i);
1480 case LABEL_COUNTER_SUBPARAGRAPH:
1481 if (par->isRightToLeftPar(buf->params))
1482 s << hebrewCounter(par->getCounter(i-5));
1484 s << alphaCounter(par->getCounter(i-5));
1487 << par->getCounter(i-4) << '.'
1488 << par->getCounter(i-3) << '.'
1489 << par->getCounter(i-2) << '.'
1490 << par->getCounter(i-1) << '.'
1491 << par->getCounter(i);
1495 // Can this ever be reached? And in the
1496 // case it is, how can this be correct?
1498 s << par->getCounter(i) << '.';
1504 par->params().labelString(par->params().labelString() +s.str().c_str());
1505 // We really want to remove the c_str as soon as
1508 for (i++; i < 10; ++i) {
1509 // reset the following counters
1510 par->setCounter(i, 0);
1512 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1513 for (i++; i < 10; ++i) {
1514 // reset the following counters
1515 par->setCounter(i, 0);
1517 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1518 par->incCounter(i + par->enumdepth);
1519 int number = par->getCounter(i + par->enumdepth);
1523 switch (par->enumdepth) {
1525 if (par->isRightToLeftPar(buf->params))
1527 << hebrewCounter(number)
1531 << loweralphaCounter(number)
1535 if (par->isRightToLeftPar(buf->params))
1536 s << '.' << romanCounter(number);
1538 s << romanCounter(number) << '.';
1541 if (par->isRightToLeftPar(buf->params))
1543 << alphaCounter(number);
1545 s << alphaCounter(number)
1549 if (par->isRightToLeftPar(buf->params))
1556 par->params().labelString(s.str().c_str());
1558 for (i += par->enumdepth + 1; i < 10; ++i) {
1559 // reset the following counters
1560 par->setCounter(i, 0);
1564 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1565 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1567 int number = par->getCounter(i);
1569 InsetCommandParams p("bibitem" );
1570 par->bibkey = new InsetBibKey(p);
1572 par->bibkey->setCounter(number);
1573 par->params().labelString(layout.labelstring());
1575 // In biblio should't be following counters but...
1577 string s = layout.labelstring();
1579 // the caption hack:
1580 if (layout.labeltype == LABEL_SENSITIVE) {
1581 bool isOK (par->inInset() && par->inInset()->owner() &&
1582 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1585 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1587 = floatList.getType(tmp->type());
1588 // We should get the correct number here too.
1589 s = fl.name() + " #:";
1591 /* par->SetLayout(0);
1592 s = layout->labelstring; */
1593 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1594 ? " :úåòîùî øñç" : "Senseless: ";
1597 par->params().labelString(s);
1599 /* reset the enumeration counter. They are always resetted
1600 * when there is any other layout between */
1601 for (int i = 6 + par->enumdepth; i < 10; ++i)
1602 par->setCounter(i, 0);
1607 // Updates all counters BEHIND the row. Changed paragraphs
1608 // with a dynamic left margin will be rebroken.
1609 void LyXText::updateCounters(BufferView * bview, Row * row) const
1617 par = row->par()->next();
1621 while (row->par() != par)
1624 setCounter(bview->buffer(), par);
1626 // now check for the headline layouts. remember that they
1627 // have a dynamic left margin
1628 LyXTextClass const & tclass =
1629 textclasslist[bview->buffer()->params.textclass];
1630 LyXLayout const & layout = tclass[par->layout()];
1632 if (layout.margintype == MARGIN_DYNAMIC
1633 || layout.labeltype == LABEL_SENSITIVE) {
1634 // Rebreak the paragraph
1635 removeParagraph(row);
1636 appendParagraph(bview, row);
1643 void LyXText::insertInset(BufferView * bview, Inset * inset)
1645 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1647 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1649 cursor.par()->insertInset(cursor.pos(), inset);
1650 // Just to rebreak and refresh correctly.
1651 // The character will not be inserted a second time
1652 insertChar(bview, Paragraph::META_INSET);
1653 // If we enter a highly editable inset the cursor should be to before
1654 // the inset. This couldn't happen before as Undo was not handled inside
1655 // inset now after the Undo LyX tries to call inset->Edit(...) again
1656 // and cannot do this as the cursor is behind the inset and GetInset
1657 // does not return the inset!
1658 if (isHighlyEditableInset(inset)) {
1659 cursorLeft(bview, true);
1665 void LyXText::copyEnvironmentType()
1667 copylayouttype = cursor.par()->layout();
1671 void LyXText::pasteEnvironmentType(BufferView * bview)
1673 setLayout(bview, copylayouttype);
1677 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1679 // Stuff what we got on the clipboard. Even if there is no selection.
1681 // There is a problem with having the stuffing here in that the
1682 // larger the selection the slower LyX will get. This can be
1683 // solved by running the line below only when the selection has
1684 // finished. The solution used currently just works, to make it
1685 // faster we need to be more clever and probably also have more
1686 // calls to stuffClipboard. (Lgb)
1687 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1689 // This doesn't make sense, if there is no selection
1690 if (!selection.set())
1693 // OK, we have a selection. This is always between selection.start
1694 // and selection.end
1696 // make sure that the depth behind the selection are restored, too
1697 Paragraph * endpar = selection.end.par()->next();
1698 Paragraph * undoendpar = endpar;
1700 if (endpar && endpar->getDepth()) {
1701 while (endpar && endpar->getDepth()) {
1702 endpar = endpar->next();
1703 undoendpar = endpar;
1705 } else if (endpar) {
1706 endpar = endpar->next(); // because of parindents etc.
1709 setUndo(bview, Undo::DELETE,
1710 selection.start.par(), undoendpar);
1712 // there are two cases: cut only within one paragraph or
1713 // more than one paragraph
1714 if (selection.start.par() == selection.end.par()) {
1715 // only within one paragraph
1716 endpar = selection.end.par();
1717 int pos = selection.end.pos();
1718 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1719 selection.start.pos(), pos,
1720 bview->buffer()->params.textclass,
1722 selection.end.pos(pos);
1724 endpar = selection.end.par();
1725 int pos = selection.end.pos();
1726 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1727 selection.start.pos(), pos,
1728 bview->buffer()->params.textclass,
1731 selection.end.par(endpar);
1732 selection.end.pos(pos);
1733 cursor.pos(selection.end.pos());
1735 endpar = endpar->next();
1737 // sometimes necessary
1739 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1741 redoParagraphs(bview, selection.start, endpar);
1743 // cutSelection can invalidate the cursor so we need to set
1745 cursor = selection.start;
1747 // need a valid cursor. (Lgb)
1750 setCursor(bview, cursor.par(), cursor.pos());
1751 selection.cursor = cursor;
1752 updateCounters(bview, cursor.row());
1756 void LyXText::copySelection(BufferView * bview)
1758 // stuff the selection onto the X clipboard, from an explicit copy request
1759 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1761 // this doesnt make sense, if there is no selection
1762 if (!selection.set())
1765 // ok we have a selection. This is always between selection.start
1766 // and sel_end cursor
1768 // copy behind a space if there is one
1769 while (selection.start.par()->size() > selection.start.pos()
1770 && selection.start.par()->isLineSeparator(selection.start.pos())
1771 && (selection.start.par() != selection.end.par()
1772 || selection.start.pos() < selection.end.pos()))
1773 selection.start.pos(selection.start.pos() + 1);
1775 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1776 selection.start.pos(), selection.end.pos(),
1777 bview->buffer()->params.textclass);
1781 void LyXText::pasteSelection(BufferView * bview)
1783 // this does not make sense, if there is nothing to paste
1784 if (!CutAndPaste::checkPastePossible(cursor.par()))
1787 setUndo(bview, Undo::INSERT,
1788 cursor.par(), cursor.par()->next());
1791 Paragraph * actpar = cursor.par();
1792 int pos = cursor.pos();
1794 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1795 bview->buffer()->params.textclass);
1797 redoParagraphs(bview, cursor, endpar);
1799 setCursor(bview, cursor.par(), cursor.pos());
1802 selection.cursor = cursor;
1803 setCursor(bview, actpar, pos);
1804 setSelection(bview);
1805 updateCounters(bview, cursor.row());
1809 // sets the selection over the number of characters of string, no check!!
1810 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1815 selection.cursor = cursor;
1816 for (string::size_type i = 0; i < str.length(); ++i)
1818 setSelection(bview);
1822 // simple replacing. The font of the first selected character is used
1823 void LyXText::replaceSelectionWithString(BufferView * bview,
1826 setCursorParUndo(bview);
1829 if (!selection.set()) { // create a dummy selection
1830 selection.end = cursor;
1831 selection.start = cursor;
1834 // Get font setting before we cut
1835 pos_type pos = selection.end.pos();
1836 LyXFont const font = selection.start.par()
1837 ->getFontSettings(bview->buffer()->params,
1838 selection.start.pos());
1840 // Insert the new string
1841 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1842 selection.end.par()->insertChar(pos, (*cit), font);
1846 // Cut the selection
1847 cutSelection(bview, true, false);
1853 // needed to insert the selection
1854 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1856 Paragraph * par = cursor.par();
1857 pos_type pos = cursor.pos();
1858 Paragraph * endpar = cursor.par()->next();
1860 setCursorParUndo(bview);
1862 // only to be sure, should not be neccessary
1865 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1867 redoParagraphs(bview, cursor, endpar);
1868 setCursor(bview, cursor.par(), cursor.pos());
1869 selection.cursor = cursor;
1870 setCursor(bview, par, pos);
1871 setSelection(bview);
1875 // turns double-CR to single CR, others where converted into one
1876 // blank. Then InsertStringAsLines is called
1877 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1879 string linestr(str);
1880 bool newline_inserted = false;
1881 for (string::size_type i = 0; i < linestr.length(); ++i) {
1882 if (linestr[i] == '\n') {
1883 if (newline_inserted) {
1884 // we know that \r will be ignored by
1885 // InsertStringA. Of course, it is a dirty
1886 // trick, but it works...
1887 linestr[i - 1] = '\r';
1891 newline_inserted = true;
1893 } else if (IsPrintable(linestr[i])) {
1894 newline_inserted = false;
1897 insertStringAsLines(bview, linestr);
1901 bool LyXText::gotoNextInset(BufferView * bview,
1902 vector<Inset::Code> const & codes,
1903 string const & contents) const
1905 LyXCursor res = cursor;
1908 if (res.pos() < res.par()->size() - 1) {
1909 res.pos(res.pos() + 1);
1911 res.par(res.par()->next());
1915 } while (res.par() &&
1916 !(res.par()->isInset(res.pos())
1917 && (inset = res.par()->getInset(res.pos())) != 0
1918 && find(codes.begin(), codes.end(), inset->lyxCode())
1920 && (contents.empty() ||
1921 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1925 setCursor(bview, res.par(), res.pos(), false);
1932 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1935 LyXCursor tmpcursor;
1939 Row * row = getRow(par, pos, y);
1941 // is there a break one row above
1942 if (row->previous() && row->previous()->par() == row->par()) {
1943 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1944 if (z >= row->pos()) {
1945 // set the dimensions of the row above
1946 y -= row->previous()->height();
1948 refresh_row = row->previous();
1949 status(bview, LyXText::NEED_MORE_REFRESH);
1951 breakAgain(bview, row->previous());
1953 // set the cursor again. Otherwise
1954 // dangling pointers are possible
1955 setCursor(bview, cursor.par(), cursor.pos(),
1956 false, cursor.boundary());
1957 selection.cursor = cursor;
1962 int const tmpheight = row->height();
1963 pos_type const tmplast = rowLast(row);
1967 breakAgain(bview, row);
1968 if (row->height() == tmpheight && rowLast(row) == tmplast)
1969 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1971 status(bview, LyXText::NEED_MORE_REFRESH);
1973 // check the special right address boxes
1974 if (textclasslist[bview->buffer()->params.textclass][
1975 par->layout()].margintype
1976 == MARGIN_RIGHT_ADDRESS_BOX)
1984 redoDrawingOfParagraph(bview, tmpcursor);
1987 // set the cursor again. Otherwise dangling pointers are possible
1988 // also set the selection
1990 if (selection.set()) {
1992 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1993 false, selection.cursor.boundary());
1994 selection.cursor = cursor;
1995 setCursorIntern(bview, selection.start.par(),
1996 selection.start.pos(),
1997 false, selection.start.boundary());
1998 selection.start = cursor;
1999 setCursorIntern(bview, selection.end.par(),
2000 selection.end.pos(),
2001 false, selection.end.boundary());
2002 selection.end = cursor;
2003 setCursorIntern(bview, last_sel_cursor.par(),
2004 last_sel_cursor.pos(),
2005 false, last_sel_cursor.boundary());
2006 last_sel_cursor = cursor;
2009 setCursorIntern(bview, cursor.par(), cursor.pos(),
2010 false, cursor.boundary());
2014 // returns false if inset wasn't found
2015 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2017 // first check the current paragraph
2018 int pos = cursor.par()->getPositionOfInset(inset);
2020 checkParagraph(bview, cursor.par(), pos);
2024 // check every paragraph
2026 Paragraph * par = ownerParagraph();
2028 pos = par->getPositionOfInset(inset);
2030 checkParagraph(bview, par, pos);
2040 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2042 bool setfont, bool boundary) const
2044 LyXCursor old_cursor = cursor;
2045 setCursorIntern(bview, par, pos, setfont, boundary);
2046 return deleteEmptyParagraphMechanism(bview, old_cursor);
2050 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2051 pos_type pos, bool boundary) const
2058 cur.boundary(boundary);
2060 // get the cursor y position in text
2062 Row * row = getRow(par, pos, y);
2063 Row * old_row = row;
2065 // if we are before the first char of this row and are still in the
2066 // same paragraph and there is a previous row then put the cursor on
2067 // the end of the previous row
2068 cur.iy(y + row->baseline());
2070 if (row->previous() && pos &&
2071 row->previous()->par() == row->par() &&
2072 par->getChar(pos) == Paragraph::META_INSET &&
2073 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2075 row = row->previous();
2080 // y is now the beginning of the cursor row
2081 y += row->baseline();
2082 // y is now the cursor baseline
2085 pos_type last = rowLastPrintable(old_row);
2087 if (pos > last + 1) {
2088 // This shouldn't happen.
2091 } else if (pos < row->pos()) {
2096 // now get the cursors x position
2097 float x = getCursorX(bview, row, pos, last, boundary);
2100 if (old_row != row) {
2101 x = getCursorX(bview, old_row, pos, last, boundary);
2108 float LyXText::getCursorX(BufferView * bview, Row * row,
2109 pos_type pos, pos_type last, bool boundary) const
2111 pos_type cursor_vpos = 0;
2113 float fill_separator;
2115 float fill_label_hfill;
2116 // This call HAS to be here because of the BidiTables!!!
2117 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2120 if (last < row->pos())
2121 cursor_vpos = row->pos();
2122 else if (pos > last && !boundary)
2123 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2124 ? row->pos() : last + 1;
2125 else if (pos > row->pos() &&
2126 (pos > last || boundary))
2127 /// Place cursor after char at (logical) position pos - 1
2128 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2129 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2131 /// Place cursor before char at (logical) position pos
2132 cursor_vpos = (bidi_level(pos) % 2 == 0)
2133 ? log2vis(pos) : log2vis(pos) + 1;
2135 pos_type main_body =
2136 beginningOfMainBody(bview->buffer(), row->par());
2137 if ((main_body > 0) &&
2138 ((main_body-1 > last) ||
2139 !row->par()->isLineSeparator(main_body-1)))
2142 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2143 pos_type pos = vis2log(vpos);
2144 if (main_body > 0 && pos == main_body - 1) {
2145 x += fill_label_hfill +
2146 font_metrics::width(textclasslist[
2147 bview->buffer()->params.textclass][
2148 row->par()->layout()]
2150 getLabelFont(bview->buffer(), row->par()));
2151 if (row->par()->isLineSeparator(main_body-1))
2152 x -= singleWidth(bview, row->par(),main_body-1);
2154 if (hfillExpansion(bview->buffer(), row, pos)) {
2155 x += singleWidth(bview, row->par(), pos);
2156 if (pos >= main_body)
2159 x += fill_label_hfill;
2160 } else if (row->par()->isSeparator(pos)) {
2161 x += singleWidth(bview, row->par(), pos);
2162 if (pos >= main_body)
2163 x += fill_separator;
2165 x += singleWidth(bview, row->par(), pos);
2171 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2172 pos_type pos, bool setfont, bool boundary) const
2174 InsetText * it = static_cast<InsetText *>(par->inInset());
2176 if (it != inset_owner) {
2177 lyxerr[Debug::INSETS] << "InsetText is " << it
2179 << "inset_owner is "
2180 << inset_owner << endl;
2181 #ifdef WITH_WARNINGS
2182 #warning I believe this code is wrong. (Lgb)
2183 #warning Jürgen, have a look at this. (Lgb)
2184 #warning Hmmm, I guess you are right but we
2185 #warning should verify when this is needed
2187 // Jürgen, would you like to have a look?
2188 // I guess we need to move the outer cursor
2189 // and open and lock the inset (bla bla bla)
2190 // stuff I don't know... so can you have a look?
2192 // I moved the lyxerr stuff in here so we can see if
2193 // this is actually really needed and where!
2195 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2200 setCursor(bview, cursor, par, pos, boundary);
2202 setCurrentFont(bview);
2206 void LyXText::setCurrentFont(BufferView * bview) const
2208 pos_type pos = cursor.pos();
2209 if (cursor.boundary() && pos > 0)
2213 if (pos == cursor.par()->size())
2215 else // potentional bug... BUG (Lgb)
2216 if (cursor.par()->isSeparator(pos)) {
2217 if (pos > cursor.row()->pos() &&
2218 bidi_level(pos) % 2 ==
2219 bidi_level(pos - 1) % 2)
2221 else if (pos + 1 < cursor.par()->size())
2227 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2228 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2230 if (cursor.pos() == cursor.par()->size() &&
2231 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2232 !cursor.boundary()) {
2233 Language const * lang =
2234 cursor.par()->getParLanguage(bview->buffer()->params);
2235 current_font.setLanguage(lang);
2236 current_font.setNumber(LyXFont::OFF);
2237 real_current_font.setLanguage(lang);
2238 real_current_font.setNumber(LyXFont::OFF);
2243 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2245 LyXCursor old_cursor = cursor;
2247 setCursorFromCoordinates(bview, cursor, x, y);
2248 setCurrentFont(bview);
2249 deleteEmptyParagraphMechanism(bview, old_cursor);
2253 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2256 // Get the row first.
2258 Row * row = getRowNearY(y);
2260 pos_type const column = getColumnNearX(bview, row, x, bound);
2261 cur.par(row->par());
2262 cur.pos(row->pos() + column);
2264 cur.y(y + row->baseline());
2267 if (row->next() && row->next()->pos() == cur.pos() &&
2268 cur.par() == row->next()->par() &&
2269 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2270 (ins=cur.par()->getInset(cur.pos())) &&
2271 (ins->needFullRow() || ins->display()))
2273 // we enter here if we put the cursor on the end of the row before
2274 // a inset which uses a full row and in that case we HAVE to calculate
2275 // the right (i) values.
2276 pos_type last = rowLastPrintable(row);
2277 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2279 cur.iy(y + row->height() + row->next()->baseline());
2280 cur.irow(row->next());
2286 cur.boundary(bound);
2290 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2292 if (cursor.pos() > 0) {
2293 bool boundary = cursor.boundary();
2294 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2295 if (!internal && !boundary &&
2296 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2297 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2298 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2299 Paragraph * par = cursor.par()->previous();
2300 setCursor(bview, par, par->size());
2305 void LyXText::cursorRight(BufferView * bview, bool internal) const
2307 if (!internal && cursor.boundary() &&
2308 !cursor.par()->isNewline(cursor.pos()))
2309 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2310 else if (cursor.pos() < cursor.par()->size()) {
2311 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2313 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2314 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2315 } else if (cursor.par()->next())
2316 setCursor(bview, cursor.par()->next(), 0);
2320 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2323 int x = cursor.x_fix();
2324 int y = cursor.y() - cursor.row()->baseline() - 1;
2325 setCursorFromCoordinates(bview, x, y);
2327 int y1 = cursor.iy() - first_y;
2331 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2332 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2333 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2337 setCursorFromCoordinates(bview, cursor.x_fix(),
2338 cursor.y() - cursor.row()->baseline() - 1);
2343 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2346 int x = cursor.x_fix();
2347 int y = cursor.y() - cursor.row()->baseline() +
2348 cursor.row()->height() + 1;
2349 setCursorFromCoordinates(bview, x, y);
2350 if (!selecting && cursor.row() == cursor.irow()) {
2351 int y1 = cursor.iy() - first_y;
2355 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2356 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2357 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2361 setCursorFromCoordinates(bview, cursor.x_fix(),
2362 cursor.y() - cursor.row()->baseline()
2363 + cursor.row()->height() + 1);
2368 void LyXText::cursorUpParagraph(BufferView * bview) const
2370 if (cursor.pos() > 0) {
2371 setCursor(bview, cursor.par(), 0);
2373 else if (cursor.par()->previous()) {
2374 setCursor(bview, cursor.par()->previous(), 0);
2379 void LyXText::cursorDownParagraph(BufferView * bview) const
2381 if (cursor.par()->next()) {
2382 setCursor(bview, cursor.par()->next(), 0);
2384 setCursor(bview, cursor.par(), cursor.par()->size());
2388 // fix the cursor `cur' after a characters has been deleted at `where'
2389 // position. Called by deleteEmptyParagraphMechanism
2390 void LyXText::fixCursorAfterDelete(BufferView * bview,
2392 LyXCursor const & where) const
2394 // if cursor is not in the paragraph where the delete occured,
2396 if (cur.par() != where.par())
2399 // if cursor position is after the place where the delete occured,
2401 if (cur.pos() > where.pos())
2402 cur.pos(cur.pos()-1);
2404 // check also if we don't want to set the cursor on a spot behind the
2405 // pagragraph because we erased the last character.
2406 if (cur.pos() > cur.par()->size())
2407 cur.pos(cur.par()->size());
2409 // recompute row et al. for this cursor
2410 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2414 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2415 LyXCursor const & old_cursor) const
2417 // Would be wrong to delete anything if we have a selection.
2418 if (selection.set())
2421 // We allow all kinds of "mumbo-jumbo" when freespacing.
2422 if (textclasslist[bview->buffer()->params.textclass][
2423 old_cursor.par()->layout()].free_spacing
2424 || old_cursor.par()->isFreeSpacing())
2429 /* Ok I'll put some comments here about what is missing.
2430 I have fixed BackSpace (and thus Delete) to not delete
2431 double-spaces automagically. I have also changed Cut,
2432 Copy and Paste to hopefully do some sensible things.
2433 There are still some small problems that can lead to
2434 double spaces stored in the document file or space at
2435 the beginning of paragraphs. This happens if you have
2436 the cursor betwenn to spaces and then save. Or if you
2437 cut and paste and the selection have a space at the
2438 beginning and then save right after the paste. I am
2439 sure none of these are very hard to fix, but I will
2440 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2441 that I can get some feedback. (Lgb)
2444 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2445 // delete the LineSeparator.
2448 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2449 // delete the LineSeparator.
2452 // If the pos around the old_cursor were spaces, delete one of them.
2453 if (old_cursor.par() != cursor.par()
2454 || old_cursor.pos() != cursor.pos()) {
2455 // Only if the cursor has really moved
2457 if (old_cursor.pos() > 0
2458 && old_cursor.pos() < old_cursor.par()->size()
2459 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2460 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2461 old_cursor.par()->erase(old_cursor.pos() - 1);
2462 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2464 #ifdef WITH_WARNINGS
2465 #warning This will not work anymore when we have multiple views of the same buffer
2466 // In this case, we will have to correct also the cursors held by
2467 // other bufferviews. It will probably be easier to do that in a more
2468 // automated way in LyXCursor code. (JMarc 26/09/2001)
2470 // correct all cursors held by the LyXText
2471 fixCursorAfterDelete(bview, cursor, old_cursor);
2472 fixCursorAfterDelete(bview, selection.cursor,
2474 fixCursorAfterDelete(bview, selection.start,
2476 fixCursorAfterDelete(bview, selection.end, old_cursor);
2477 fixCursorAfterDelete(bview, last_sel_cursor,
2479 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2480 fixCursorAfterDelete(bview, toggle_end_cursor,
2486 // don't delete anything if this is the ONLY paragraph!
2487 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2490 // Do not delete empty paragraphs with keepempty set.
2492 [bview->buffer()->params.textclass]
2493 [old_cursor.par()->layout()].keepempty)
2496 // only do our magic if we changed paragraph
2497 if (old_cursor.par() == cursor.par())
2500 // record if we have deleted a paragraph
2501 // we can't possibly have deleted a paragraph before this point
2502 bool deleted = false;
2504 if ((old_cursor.par()->size() == 0
2505 || (old_cursor.par()->size() == 1
2506 && old_cursor.par()->isLineSeparator(0)))) {
2507 // ok, we will delete anything
2508 LyXCursor tmpcursor;
2510 // make sure that you do not delete any environments
2511 status(bview, LyXText::NEED_MORE_REFRESH);
2514 if (old_cursor.row()->previous()) {
2515 refresh_row = old_cursor.row()->previous();
2516 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2518 cursor = old_cursor; // that undo can restore the right cursor position
2519 Paragraph * endpar = old_cursor.par()->next();
2520 if (endpar && endpar->getDepth()) {
2521 while (endpar && endpar->getDepth()) {
2522 endpar = endpar->next();
2525 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2529 removeRow(old_cursor.row());
2530 if (ownerParagraph() == old_cursor.par()) {
2531 ownerParagraph(ownerParagraph()->next());
2534 delete old_cursor.par();
2536 /* Breakagain the next par. Needed because of
2537 * the parindent that can occur or dissappear.
2538 * The next row can change its height, if
2539 * there is another layout before */
2540 if (refresh_row->next()) {
2541 breakAgain(bview, refresh_row->next());
2542 updateCounters(bview, refresh_row);
2544 setHeightOfRow(bview, refresh_row);
2546 refresh_row = old_cursor.row()->next();
2547 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2550 cursor = old_cursor; // that undo can restore the right cursor position
2551 Paragraph * endpar = old_cursor.par()->next();
2552 if (endpar && endpar->getDepth()) {
2553 while (endpar && endpar->getDepth()) {
2554 endpar = endpar->next();
2557 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2561 removeRow(old_cursor.row());
2563 if (ownerParagraph() == old_cursor.par()) {
2564 ownerParagraph(ownerParagraph()->next());
2567 delete old_cursor.par();
2569 /* Breakagain the next par. Needed because of
2570 the parindent that can occur or dissappear.
2571 The next row can change its height, if
2572 there is another layout before */
2574 breakAgain(bview, refresh_row);
2575 updateCounters(bview, refresh_row->previous());
2580 setCursorIntern(bview, cursor.par(), cursor.pos());
2582 if (selection.cursor.par() == old_cursor.par()
2583 && selection.cursor.pos() == old_cursor.pos()) {
2584 // correct selection
2585 selection.cursor = cursor;
2589 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2590 redoParagraphs(bview, old_cursor,
2591 old_cursor.par()->next());
2593 setCursorIntern(bview, cursor.par(), cursor.pos());
2594 selection.cursor = cursor;
2601 void LyXText::toggleAppendix(BufferView * bview)
2603 Paragraph * par = cursor.par();
2604 bool start = !par->params().startOfAppendix();
2606 // ensure that we have only one start_of_appendix in this document
2607 Paragraph * tmp = ownerParagraph();
2608 for (; tmp; tmp = tmp->next()) {
2609 tmp->params().startOfAppendix(false);
2612 par->params().startOfAppendix(start);
2614 // we can set the refreshing parameters now
2615 status(bview, LyXText::NEED_MORE_REFRESH);
2617 refresh_row = 0; // not needed for full update
2618 updateCounters(bview, 0);
2619 setCursor(bview, cursor.par(), cursor.pos());
2623 Paragraph * LyXText::ownerParagraph() const
2626 return inset_owner->paragraph();
2628 return bv_owner->buffer()->paragraph;
2632 void LyXText::ownerParagraph(Paragraph * p) const
2635 inset_owner->paragraph(p);
2637 bv_owner->buffer()->paragraph = p;
2642 void LyXText::ownerParagraph(int id, Paragraph * p) const
2644 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2645 if (op && op->inInset()) {
2646 static_cast<InsetText *>(op->inInset())->paragraph(p);
2653 LyXText::text_status LyXText::status() const
2659 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2661 // We should only go up with refreshing code so this means that if
2662 // we have a MORE refresh we should never set it to LITTLE if we still
2663 // didn't handle it (and then it will be UNCHANGED. Now as long as
2664 // we stay inside one LyXText this may work but we need to tell the
2665 // outermost LyXText that it should REALLY draw us if there is some
2666 // change in a Inset::LyXText. So you see that when we are inside a
2667 // inset's LyXText we give the LITTLE to the outermost LyXText to
2668 // tell'em that it should redraw the actual row (where the inset
2669 // resides! Capito?!
2671 if ((status_ != NEED_MORE_REFRESH)
2672 || (status_ == NEED_MORE_REFRESH
2673 && st != NEED_VERY_LITTLE_REFRESH))
2676 if (inset_owner && st != UNCHANGED) {
2677 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2678 if (!bview->text->refresh_row) {
2679 bview->text->refresh_row = bview->text->cursor.row();
2680 bview->text->refresh_y = bview->text->cursor.y() -
2681 bview->text->cursor.row()->baseline();