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 "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
27 #include "support/textutils.h"
28 #include "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
41 #include "FloatList.h"
43 #include "ParagraphParameters.h"
52 LyXText::LyXText(BufferView * bv)
53 : number_of_rows(0), height(0), width(0), first(0),
54 bv_owner(bv), inset_owner(0), the_locking_inset(0),
55 need_break_row(0), refresh_y(0), refresh_row(0),
56 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
61 LyXText::LyXText(InsetText * inset)
62 : number_of_rows(0), height(0), width(0), first(0),
63 bv_owner(0), inset_owner(inset), the_locking_inset(0),
64 need_break_row(0), refresh_y(0), refresh_row(0),
65 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
69 void LyXText::init(BufferView * bview, bool reinit)
72 // Delete all rows, this does not touch the paragraphs!
73 Row * tmprow = firstrow;
75 tmprow = firstrow->next();
79 lastrow = refresh_row = need_break_row = 0;
80 width = height = copylayouttype = 0;
81 number_of_rows = first = refresh_y = 0;
82 status_ = LyXText::UNCHANGED;
86 Paragraph * par = ownerParagraph();
87 current_font = getFont(bview->buffer(), par, 0);
89 insertParagraph(bview, par, lastrow);
92 setCursorIntern(bview, firstrow->par(), 0);
93 selection.cursor = cursor;
99 // Delete all rows, this does not touch the paragraphs!
100 Row * tmprow = firstrow;
102 tmprow = firstrow->next();
109 // Gets the fully instantiated font at a given position in a paragraph
110 // Basically the same routine as Paragraph::getFont() in paragraph.C.
111 // The difference is that this one is used for displaying, and thus we
112 // are allowed to make cosmetic improvements. For instance make footnotes
114 // If position is -1, we get the layout font of the paragraph.
115 // If position is -2, we get the font of the manual label of the paragraph.
116 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
117 Paragraph::size_type pos) const
119 LyXLayout const & layout =
120 textclasslist.Style(buf->params.textclass, par->getLayout());
122 Paragraph::depth_type par_depth = par->getDepth();
123 // We specialize the 95% common case:
127 if (layout.labeltype == LABEL_MANUAL
128 && pos < beginningOfMainBody(buf, par)) {
130 LyXFont f = par->getFontSettings(buf->params,
132 return f.realize(layout.reslabelfont, buf->params.language);
134 LyXFont f = par->getFontSettings(buf->params, pos);
135 return f.realize(layout.resfont, buf->params.language);
140 // process layoutfont for pos == -1 and labelfont for pos < -1
142 return layout.resfont;
144 return layout.reslabelfont;
148 // The uncommon case need not be optimized as much
150 LyXFont layoutfont, tmpfont;
154 if (pos < beginningOfMainBody(buf, par)) {
156 layoutfont = layout.labelfont;
159 layoutfont = layout.font;
161 tmpfont = par->getFontSettings(buf->params, pos);
162 tmpfont.realize(layoutfont, buf->params.language);
165 // process layoutfont for pos == -1 and labelfont for pos < -1
167 tmpfont = layout.font;
169 tmpfont = layout.labelfont;
172 // Resolve against environment font information
173 while (par && par_depth && !tmpfont.resolved()) {
174 par = par->outerHook();
176 tmpfont.realize(textclasslist.
177 Style(buf->params.textclass,
178 par->getLayout()).font,
179 buf->params.language);
180 par_depth = par->getDepth();
184 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
185 buf->params.language);
191 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
192 Paragraph::size_type pos, LyXFont const & fnt,
195 Buffer const * buf = bv->buffer();
196 LyXFont font = getFont(buf, par, pos);
197 font.update(fnt, toggleall);
198 // Let the insets convert their font
199 if (par->getChar(pos) == Paragraph::META_INSET) {
200 Inset * inset = par->getInset(pos);
202 if (inset->editable()==Inset::HIGHLY_EDITABLE) {
203 UpdatableInset * uinset =
204 static_cast<UpdatableInset *>(inset);
205 uinset->setFont(bv, fnt, toggleall, true);
207 font = inset->convertFont(font);
211 LyXLayout const & layout =
212 textclasslist.Style(buf->params.textclass,
215 // Get concrete layout font to reduce against
218 if (pos < beginningOfMainBody(buf, par))
219 layoutfont = layout.labelfont;
221 layoutfont = layout.font;
223 // Realize against environment font information
224 if (par->getDepth()){
225 Paragraph * tp = par;
226 while (!layoutfont.resolved() && tp && tp->getDepth()) {
227 tp = tp->outerHook();
229 layoutfont.realize(textclasslist.
230 Style(buf->params.textclass,
231 tp->getLayout()).font,
232 buf->params.language);
236 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
237 buf->params.language);
239 // Now, reduce font against full layout font
240 font.reduce(layoutfont);
242 par->setFont(pos, font);
246 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
247 Paragraph::size_type pos, LyXFont const & fnt)
250 // Let the insets convert their font
251 if (par->getChar(pos) == Paragraph::META_INSET) {
252 font = par->getInset(pos)->convertFont(font);
255 LyXLayout const & layout =
256 textclasslist.Style(buf->params.textclass,
259 // Get concrete layout font to reduce against
262 if (pos < beginningOfMainBody(buf, par))
263 layoutfont = layout.labelfont;
265 layoutfont = layout.font;
267 // Realize against environment font information
268 if (par->getDepth()){
269 Paragraph * tp = par;
270 while (!layoutfont.resolved() && tp && tp->getDepth()) {
271 tp = tp->outerHook();
273 layoutfont.realize(textclasslist.
274 Style(buf->params.textclass,
275 tp->getLayout()).font,
276 buf->params.language);
280 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
281 buf->params.language);
283 // Now, reduce font against full layout font
284 font.reduce(layoutfont);
286 par->setFont(pos, font);
290 // inserts a new row behind the specified row, increments
291 // the touched counters
292 void LyXText::insertRow(Row * row, Paragraph * par,
293 Paragraph::size_type pos) const
295 Row * tmprow = new Row;
298 tmprow->next(firstrow);
301 tmprow->previous(row);
302 tmprow->next(row->next());
307 tmprow->next()->previous(tmprow);
309 if (tmprow->previous())
310 tmprow->previous()->next(tmprow);
322 // removes the row and reset the touched counters
323 void LyXText::removeRow(Row * row) const
325 /* this must not happen before the currentrow for clear reasons.
326 so the trick is just to set the current row onto the previous
329 getRow(row->par(), row->pos(), unused_y);
332 row->next()->previous(row->previous());
333 if (!row->previous()) {
334 firstrow = row->next();
336 row->previous()->next(row->next());
339 lastrow = row->previous();
341 height -= row->height(); // the text becomes smaller
344 --number_of_rows; // one row less
348 // remove all following rows of the paragraph of the specified row.
349 void LyXText::removeParagraph(Row * row) const
351 Paragraph * tmppar = row->par();
355 while (row && row->par() == tmppar) {
356 tmprow = row->next();
363 // insert the specified paragraph behind the specified row
364 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
367 insertRow(row, par, 0); /* insert a new row, starting
370 setCounter(bview->buffer(), par); // set the counters
372 // and now append the whole paragraph behind the new row
375 appendParagraph(bview, firstrow);
377 row->next()->height(0);
378 appendParagraph(bview, row->next());
383 Inset * LyXText::getInset() const
386 if (cursor.pos() == 0 && cursor.par()->bibkey) {
387 inset = cursor.par()->bibkey;
388 } else if (cursor.pos() < cursor.par()->size()
389 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
390 inset = cursor.par()->getInset(cursor.pos());
396 void LyXText::toggleInset(BufferView * bview)
398 Inset * inset = getInset();
399 if (!inset->editable())
401 //bview->owner()->message(inset->editMessage());
403 // do we want to keep this?? (JMarc)
404 if (inset->editable() != Inset::HIGHLY_EDITABLE)
405 setCursorParUndo(bview);
407 if (inset->isOpen()) {
413 inset->open(bview, !inset->isOpen());
418 /* used in setlayout */
419 // Asger is not sure we want to do this...
420 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
423 LyXLayout const & layout =
424 textclasslist.Style(buf->params.textclass, par->getLayout());
426 LyXFont layoutfont, tmpfont;
427 for (Paragraph::size_type pos = 0;
428 pos < par->size(); ++pos) {
429 if (pos < beginningOfMainBody(buf, par))
430 layoutfont = layout.labelfont;
432 layoutfont = layout.font;
434 tmpfont = par->getFontSettings(buf->params, pos);
435 tmpfont.reduce(layoutfont);
436 par->setFont(pos, tmpfont);
441 Paragraph * LyXText::setLayout(BufferView * bview,
442 LyXCursor & cur, LyXCursor & sstart_cur,
443 LyXCursor & send_cur,
444 LyXTextClass::size_type layout)
446 Paragraph * endpar = send_cur.par()->next();
447 Paragraph * undoendpar = endpar;
449 if (endpar && endpar->getDepth()) {
450 while (endpar && endpar->getDepth()) {
451 endpar = endpar->next();
455 endpar = endpar->next(); // because of parindents etc.
458 setUndo(bview, Undo::EDIT,
459 sstart_cur.par(), undoendpar);
461 // ok we have a selection. This is always between sstart_cur
462 // and sel_end cursor
465 LyXLayout const & lyxlayout =
466 textclasslist.Style(bview->buffer()->params.textclass, layout);
468 while (cur.par() != send_cur.par()) {
469 cur.par()->setLayout(layout);
470 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
471 Paragraph * fppar = cur.par();
472 fppar->params().spaceTop(lyxlayout.fill_top ?
473 VSpace(VSpace::VFILL)
474 : VSpace(VSpace::NONE));
475 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
476 VSpace(VSpace::VFILL)
477 : VSpace(VSpace::NONE));
478 if (lyxlayout.margintype == MARGIN_MANUAL)
479 cur.par()->setLabelWidthString(lyxlayout.labelstring());
480 if (lyxlayout.labeltype != LABEL_BIBLIO
482 delete fppar->bibkey;
485 cur.par(cur.par()->next());
487 cur.par()->setLayout(layout);
488 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
489 Paragraph * fppar = cur.par();
490 fppar->params().spaceTop(lyxlayout.fill_top ?
491 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
492 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
493 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
494 if (lyxlayout.margintype == MARGIN_MANUAL)
495 cur.par()->setLabelWidthString(lyxlayout.labelstring());
496 if (lyxlayout.labeltype != LABEL_BIBLIO
498 delete fppar->bibkey;
505 // set layout over selection and make a total rebreak of those paragraphs
506 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
508 LyXCursor tmpcursor = cursor; /* store the current cursor */
510 // if there is no selection just set the layout
511 // of the current paragraph */
512 if (!selection.set()) {
513 selection.start = cursor; // dummy selection
514 selection.end = cursor;
516 Paragraph * endpar = setLayout(bview, cursor, selection.start,
517 selection.end, layout);
518 redoParagraphs(bview, selection.start, endpar);
520 // we have to reset the selection, because the
521 // geometry could have changed
522 setCursor(bview, selection.start.par(),
523 selection.start.pos(), false);
524 selection.cursor = cursor;
525 setCursor(bview, selection.end.par(), selection.end.pos(), false);
526 updateCounters(bview, cursor.row());
527 clearSelection(bview);
529 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
533 // increment depth over selection and
534 // make a total rebreak of those paragraphs
535 void LyXText::incDepth(BufferView * bview)
537 // If there is no selection, just use the current paragraph
538 if (!selection.set()) {
539 selection.start = cursor; // dummy selection
540 selection.end = cursor;
543 // We end at the next paragraph with depth 0
544 Paragraph * endpar = selection.end.par()->next();
546 Paragraph * undoendpar = endpar;
548 if (endpar && endpar->getDepth()) {
549 while (endpar && endpar->getDepth()) {
550 endpar = endpar->next();
554 endpar = endpar->next(); // because of parindents etc.
557 setUndo(bview, Undo::EDIT,
558 selection.start.par(), undoendpar);
560 LyXCursor tmpcursor = cursor; // store the current cursor
562 // ok we have a selection. This is always between sel_start_cursor
563 // and sel_end cursor
564 cursor = selection.start;
566 bool anything_changed = false;
569 // NOTE: you can't change the depth of a bibliography entry
571 textclasslist.Style(bview->buffer()->params.textclass,
572 cursor.par()->getLayout()
573 ).labeltype != LABEL_BIBLIO) {
574 Paragraph * prev = cursor.par()->previous();
577 && (prev->getDepth() - cursor.par()->getDepth() > 0
578 || (prev->getDepth() == cursor.par()->getDepth()
579 && textclasslist.Style(bview->buffer()->params.textclass,
580 prev->getLayout()).isEnvironment()))) {
581 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
582 anything_changed = true;
585 if (cursor.par() == selection.end.par())
587 cursor.par(cursor.par()->next());
590 // if nothing changed set all depth to 0
591 if (!anything_changed) {
592 cursor = selection.start;
593 while (cursor.par() != selection.end.par()) {
594 cursor.par()->params().depth(0);
595 cursor.par(cursor.par()->next());
597 cursor.par()->params().depth(0);
600 redoParagraphs(bview, selection.start, endpar);
602 // we have to reset the selection, because the
603 // geometry could have changed
604 setCursor(bview, selection.start.par(), selection.start.pos());
605 selection.cursor = cursor;
606 setCursor(bview, selection.end.par(), selection.end.pos());
607 updateCounters(bview, cursor.row());
608 clearSelection(bview);
610 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
614 // decrement depth over selection and
615 // make a total rebreak of those paragraphs
616 void LyXText::decDepth(BufferView * bview)
618 // if there is no selection just set the layout
619 // of the current paragraph
620 if (!selection.set()) {
621 selection.start = cursor; // dummy selection
622 selection.end = cursor;
624 Paragraph * endpar = selection.end.par()->next();
625 Paragraph * undoendpar = endpar;
627 if (endpar && endpar->getDepth()) {
628 while (endpar && endpar->getDepth()) {
629 endpar = endpar->next();
633 endpar = endpar->next(); // because of parindents etc.
636 setUndo(bview, Undo::EDIT,
637 selection.start.par(), undoendpar);
639 LyXCursor tmpcursor = cursor; // store the current cursor
641 // ok we have a selection. This is always between sel_start_cursor
642 // and sel_end cursor
643 cursor = selection.start;
646 if (cursor.par()->params().depth()) {
647 cursor.par()->params()
648 .depth(cursor.par()->params().depth() - 1);
650 if (cursor.par() == selection.end.par()) {
653 cursor.par(cursor.par()->next());
656 redoParagraphs(bview, selection.start, endpar);
658 // we have to reset the selection, because the
659 // geometry could have changed
660 setCursor(bview, selection.start.par(),
661 selection.start.pos());
662 selection.cursor = cursor;
663 setCursor(bview, selection.end.par(), selection.end.pos());
664 updateCounters(bview, cursor.row());
665 clearSelection(bview);
667 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
671 // set font over selection and make a total rebreak of those paragraphs
672 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
674 // if there is no selection just set the current_font
675 if (!selection.set()) {
676 // Determine basis font
678 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
680 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
682 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
683 // Update current font
684 real_current_font.update(font, toggleall);
686 // Reduce to implicit settings
687 current_font = real_current_font;
688 current_font.reduce(layoutfont);
689 // And resolve it completely
690 real_current_font.realize(layoutfont,
691 bview->buffer()->params.language);
695 LyXCursor tmpcursor = cursor; // store the current cursor
697 // ok we have a selection. This is always between sel_start_cursor
698 // and sel_end cursor
700 setUndo(bview, Undo::EDIT,
701 selection.start.par(),
702 selection.end.par()->next());
704 cursor = selection.start;
705 while (cursor.par() != selection.end.par() ||
706 (cursor.pos() < selection.end.pos())) {
707 if (cursor.pos() < cursor.par()->size()) {
708 // an open footnote should behave
710 setCharFont(bview, cursor.par(), cursor.pos(),
712 cursor.pos(cursor.pos() + 1);
715 cursor.par(cursor.par()->next());
720 redoParagraphs(bview, selection.start, selection.end.par()->next());
722 // we have to reset the selection, because the
723 // geometry could have changed
724 setCursor(bview, selection.start.par(), selection.start.pos());
725 selection.cursor = cursor;
726 setCursor(bview, selection.end.par(), selection.end.pos());
727 clearSelection(bview);
729 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
730 tmpcursor.boundary());
734 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
736 Row * tmprow = cur.row();
737 int y = cur.y() - tmprow->baseline();
739 setHeightOfRow(bview, tmprow);
741 while (tmprow->previous()
742 && tmprow->previous()->par() == tmprow->par()) {
743 tmprow = tmprow->previous();
744 y -= tmprow->height();
745 setHeightOfRow(bview, tmprow);
748 // we can set the refreshing parameters now
749 status(bview, LyXText::NEED_MORE_REFRESH);
751 refresh_row = tmprow;
752 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
756 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
758 Row * tmprow = cur.row();
760 int y = cur.y() - tmprow->baseline();
761 setHeightOfRow(bview, tmprow);
763 while (tmprow->previous()
764 && tmprow->previous()->par() == tmprow->par()) {
765 tmprow = tmprow->previous();
766 y -= tmprow->height();
769 // we can set the refreshing parameters now
770 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
772 refresh_row = tmprow;
774 status(bview, LyXText::NEED_MORE_REFRESH);
775 setCursor(bview, cur.par(), cur.pos());
779 // deletes and inserts again all paragaphs between the cursor
780 // and the specified par
781 // This function is needed after SetLayout and SetFont etc.
782 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
783 Paragraph const * endpar) const
786 Paragraph * tmppar = 0;
787 Paragraph * first_phys_par = 0;
789 Row * tmprow = cur.row();
791 int y = cur.y() - tmprow->baseline();
793 if (!tmprow->previous()) {
794 // a trick/hack for UNDO
795 // Can somebody please tell me _why_ this solves
797 first_phys_par = firstParagraph();
799 first_phys_par = tmprow->par();
800 while (tmprow->previous()
801 && tmprow->previous()->par() == first_phys_par) {
802 tmprow = tmprow->previous();
803 y -= tmprow->height();
807 // we can set the refreshing parameters now
808 status(bview, LyXText::NEED_MORE_REFRESH);
810 refresh_row = tmprow->previous(); /* the real refresh row will
811 be deleted, so I store
815 tmppar = tmprow->next()->par();
818 while (tmppar != endpar) {
819 removeRow(tmprow->next());
821 tmppar = tmprow->next()->par();
826 // remove the first one
827 tmprow2 = tmprow; /* this is because tmprow->previous()
829 tmprow = tmprow->previous();
832 tmppar = first_phys_par;
836 insertParagraph(bview, tmppar, tmprow);
840 while (tmprow->next()
841 && tmprow->next()->par() == tmppar) {
842 tmprow = tmprow->next();
844 tmppar = tmppar->next();
846 } while (tmppar && tmppar != endpar);
848 // this is because of layout changes
850 refresh_y -= refresh_row->height();
851 setHeightOfRow(bview, refresh_row);
853 refresh_row = firstrow;
855 setHeightOfRow(bview, refresh_row);
858 if (tmprow && tmprow->next())
859 setHeightOfRow(bview, tmprow->next());
863 bool LyXText::fullRebreak(BufferView * bview)
869 if (need_break_row) {
870 breakAgain(bview, need_break_row);
878 // important for the screen
881 /* the cursor set functions have a special mechanism. When they
882 * realize, that you left an empty paragraph, they will delete it.
883 * They also delete the corresponding row */
885 // need the selection cursor:
886 void LyXText::setSelection(BufferView * bview)
888 bool const lsel = selection.set();
890 if (!selection.set()) {
891 last_sel_cursor = selection.cursor;
892 selection.start = selection.cursor;
893 selection.end = selection.cursor;
898 // first the toggling area
899 if (cursor.y() < last_sel_cursor.y()
900 || (cursor.y() == last_sel_cursor.y()
901 && cursor.x() < last_sel_cursor.x())) {
902 toggle_end_cursor = last_sel_cursor;
903 toggle_cursor = cursor;
905 toggle_end_cursor = cursor;
906 toggle_cursor = last_sel_cursor;
909 last_sel_cursor = cursor;
911 // and now the whole selection
913 if (selection.cursor.par() == cursor.par())
914 if (selection.cursor.pos() < cursor.pos()) {
915 selection.end = cursor;
916 selection.start = selection.cursor;
918 selection.end = selection.cursor;
919 selection.start = cursor;
921 else if (selection.cursor.y() < cursor.y() ||
922 (selection.cursor.y() == cursor.y()
923 && selection.cursor.x() < cursor.x())) {
924 selection.end = cursor;
925 selection.start = selection.cursor;
928 selection.end = selection.cursor;
929 selection.start = cursor;
932 // a selection with no contents is not a selection
933 if (selection.start.par() == selection.end.par() &&
934 selection.start.pos() == selection.end.pos())
935 selection.set(false);
937 if (inset_owner && (selection.set() || lsel))
938 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
942 string const LyXText::selectionAsString(Buffer const * buffer) const
944 if (!selection.set()) return string();
947 // Special handling if the whole selection is within one paragraph
948 if (selection.start.par() == selection.end.par()) {
949 result += selection.start.par()->asString(buffer,
950 selection.start.pos(),
951 selection.end.pos());
955 // The selection spans more than one paragraph
957 // First paragraph in selection
958 result += selection.start.par()->asString(buffer,
959 selection.start.pos(),
960 selection.start.par()->size())
963 // The paragraphs in between (if any)
964 LyXCursor tmpcur(selection.start);
965 tmpcur.par(tmpcur.par()->next());
966 while (tmpcur.par() != selection.end.par()) {
967 result += tmpcur.par()->asString(buffer, 0,
968 tmpcur.par()->size()) +"\n\n";
969 tmpcur.par(tmpcur.par()->next());
972 // Last paragraph in selection
973 result += selection.end.par()->asString(buffer, 0,
974 selection.end.pos());
980 void LyXText::clearSelection(BufferView * /*bview*/) const
982 selection.set(false);
983 selection.mark(false);
984 selection.end = selection.start = selection.cursor = cursor;
988 void LyXText::cursorHome(BufferView * bview) const
990 setCursor(bview, cursor.par(), cursor.row()->pos());
994 void LyXText::cursorEnd(BufferView * bview) const
996 if (!cursor.row()->next()
997 || cursor.row()->next()->par() != cursor.row()->par()) {
998 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1000 if (cursor.par()->size() &&
1001 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1002 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1003 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1005 setCursor(bview,cursor.par(),
1006 rowLast(cursor.row()) + 1);
1012 void LyXText::cursorTop(BufferView * bview) const
1014 while (cursor.par()->previous())
1015 cursor.par(cursor.par()->previous());
1016 setCursor(bview, cursor.par(), 0);
1020 void LyXText::cursorBottom(BufferView * bview) const
1022 while (cursor.par()->next())
1023 cursor.par(cursor.par()->next());
1024 setCursor(bview, cursor.par(), cursor.par()->size());
1028 void LyXText::toggleFree(BufferView * bview,
1029 LyXFont const & font, bool toggleall)
1031 // If the mask is completely neutral, tell user
1032 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1033 // Could only happen with user style
1034 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1038 // Try implicit word selection
1039 // If there is a change in the language the implicit word selection
1041 LyXCursor resetCursor = cursor;
1042 bool implicitSelection = (font.language() == ignore_language
1043 && font.number() == LyXFont::IGNORE)
1044 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1047 setFont(bview, font, toggleall);
1049 // Implicit selections are cleared afterwards
1050 //and cursor is set to the original position.
1051 if (implicitSelection) {
1052 clearSelection(bview);
1053 cursor = resetCursor;
1054 setCursor(bview, cursor.par(), cursor.pos());
1055 selection.cursor = cursor;
1058 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1063 LyXText::getStringToIndex(BufferView * bview)
1067 // Try implicit word selection
1068 // If there is a change in the language the implicit word selection
1070 LyXCursor resetCursor = cursor;
1071 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1073 if (!selection.set()) {
1074 bview->owner()->message(_("Nothing to index!"));
1077 if (selection.start.par() != selection.end.par()) {
1078 bview->owner()->message(_("Cannot index more than one paragraph!"));
1082 idxstring = selectionAsString(bview->buffer());
1084 // Implicit selections are cleared afterwards
1085 //and cursor is set to the original position.
1086 if (implicitSelection) {
1087 clearSelection(bview);
1088 cursor = resetCursor;
1089 setCursor(bview, cursor.par(), cursor.pos());
1090 selection.cursor = cursor;
1095 Paragraph::size_type
1096 LyXText::beginningOfMainBody(Buffer const * buf,
1097 Paragraph const * par) const
1099 if (textclasslist.Style(buf->params.textclass,
1100 par->getLayout()).labeltype != LABEL_MANUAL)
1103 return par->beginningOfMainBody();
1107 /* the DTP switches for paragraphs. LyX will store them in the
1108 * first physicla paragraph. When a paragraph is broken, the top settings
1109 * rest, the bottom settings are given to the new one. So I can make shure,
1110 * they do not duplicate themself and you cannnot make dirty things with
1113 void LyXText::setParagraph(BufferView * bview,
1114 bool line_top, bool line_bottom,
1115 bool pagebreak_top, bool pagebreak_bottom,
1116 VSpace const & space_top,
1117 VSpace const & space_bottom,
1119 string labelwidthstring,
1122 LyXCursor tmpcursor = cursor;
1123 if (!selection.set()) {
1124 selection.start = cursor;
1125 selection.end = cursor;
1128 // make sure that the depth behind the selection are restored, too
1129 Paragraph * endpar = selection.end.par()->next();
1130 Paragraph * undoendpar = endpar;
1132 if (endpar && endpar->getDepth()) {
1133 while (endpar && endpar->getDepth()) {
1134 endpar = endpar->next();
1135 undoendpar = endpar;
1139 // because of parindents etc.
1140 endpar = endpar->next();
1143 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1146 Paragraph * tmppar = selection.end.par();
1147 while (tmppar != selection.start.par()->previous()) {
1148 setCursor(bview, tmppar, 0);
1149 status(bview, LyXText::NEED_MORE_REFRESH);
1150 refresh_row = cursor.row();
1151 refresh_y = cursor.y() - cursor.row()->baseline();
1152 cursor.par()->params().lineTop(line_top);
1153 cursor.par()->params().lineBottom(line_bottom);
1154 cursor.par()->params().pagebreakTop(pagebreak_top);
1155 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1156 cursor.par()->params().spaceTop(space_top);
1157 cursor.par()->params().spaceBottom(space_bottom);
1158 // does the layout allow the new alignment?
1159 if (align == LYX_ALIGN_LAYOUT)
1160 align = textclasslist
1161 .Style(bview->buffer()->params.textclass,
1162 cursor.par()->getLayout()).align;
1163 if (align & textclasslist
1164 .Style(bview->buffer()->params.textclass,
1165 cursor.par()->getLayout()).alignpossible) {
1166 if (align == textclasslist
1167 .Style(bview->buffer()->params.textclass,
1168 cursor.par()->getLayout()).align)
1169 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1171 cursor.par()->params().align(align);
1173 cursor.par()->setLabelWidthString(labelwidthstring);
1174 cursor.par()->params().noindent(noindent);
1175 tmppar = cursor.par()->previous();
1178 redoParagraphs(bview, selection.start, endpar);
1180 clearSelection(bview);
1181 setCursor(bview, selection.start.par(), selection.start.pos());
1182 selection.cursor = cursor;
1183 setCursor(bview, selection.end.par(), selection.end.pos());
1184 setSelection(bview);
1185 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1187 bview->updateInset(inset_owner, true);
1191 char loweralphaCounter(int n)
1193 if (n < 1 || n > 26)
1203 char alphaCounter(int n)
1205 if (n < 1 || n > 26)
1213 char hebrewCounter(int n)
1215 static const char hebrew[22] = {
1216 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1217 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1220 if (n < 1 || n > 22)
1228 string const romanCounter(int n)
1230 static char const * roman[20] = {
1231 "i", "ii", "iii", "iv", "v",
1232 "vi", "vii", "viii", "ix", "x",
1233 "xi", "xii", "xiii", "xiv", "xv",
1234 "xvi", "xvii", "xviii", "xix", "xx"
1236 if (n < 1 || n > 20)
1245 // set the counter of a paragraph. This includes the labels
1246 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1248 LyXLayout const & layout =
1249 textclasslist.Style(buf->params.textclass,
1252 LyXTextClass const & textclass =
1253 textclasslist.TextClass(buf->params.textclass);
1255 // copy the prev-counters to this one,
1256 // unless this is the first paragraph
1257 if (par->previous()) {
1258 for (int i = 0; i < 10; ++i) {
1259 par->setCounter(i, par->previous()->getFirstCounter(i));
1261 par->params().appendix(par->previous()->params().appendix());
1262 if (!par->params().appendix() && par->params().startOfAppendix()) {
1263 par->params().appendix(true);
1264 for (int i = 0; i < 10; ++i) {
1265 par->setCounter(i, 0);
1268 par->enumdepth = par->previous()->enumdepth;
1269 par->itemdepth = par->previous()->itemdepth;
1271 for (int i = 0; i < 10; ++i) {
1272 par->setCounter(i, 0);
1274 par->params().appendix(par->params().startOfAppendix());
1279 /* Maybe we have to increment the enumeration depth.
1280 * BUT, enumeration in a footnote is considered in isolation from its
1281 * surrounding paragraph so don't increment if this is the
1282 * first line of the footnote
1283 * AND, bibliographies can't have their depth changed ie. they
1284 * are always of depth 0
1287 && par->previous()->getDepth() < par->getDepth()
1288 && textclasslist.Style(buf->params.textclass,
1289 par->previous()->getLayout()
1290 ).labeltype == LABEL_COUNTER_ENUMI
1291 && par->enumdepth < 3
1292 && layout.labeltype != LABEL_BIBLIO) {
1296 // Maybe we have to decrement the enumeration depth, see note above
1298 && par->previous()->getDepth() > par->getDepth()
1299 && layout.labeltype != LABEL_BIBLIO) {
1300 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1301 par->setCounter(6 + par->enumdepth,
1302 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1303 /* reset the counters.
1304 * A depth change is like a breaking layout
1306 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1307 par->setCounter(i, 0);
1310 if (!par->params().labelString().empty()) {
1311 par->params().labelString(string());
1314 if (layout.margintype == MARGIN_MANUAL) {
1315 if (par->params().labelWidthString().empty()) {
1316 par->setLabelWidthString(layout.labelstring());
1319 par->setLabelWidthString(string());
1322 // is it a layout that has an automatic label?
1323 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1325 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1326 if (i >= 0 && i<= buf->params.secnumdepth) {
1327 par->incCounter(i); // increment the counter
1329 // Is there a label? Useful for Chapter layout
1330 if (!par->params().appendix()) {
1331 if (!layout.labelstring().empty())
1332 par->params().labelString(layout.labelstring());
1334 par->params().labelString(string());
1336 if (!layout.labelstring_appendix().empty())
1337 par->params().labelString(layout.labelstring_appendix());
1339 par->params().labelString(string());
1344 if (!par->params().appendix()) {
1345 switch (2 * LABEL_COUNTER_CHAPTER -
1346 textclass.maxcounter() + i) {
1347 case LABEL_COUNTER_CHAPTER:
1348 s << par->getCounter(i);
1350 case LABEL_COUNTER_SECTION:
1351 s << par->getCounter(i - 1) << '.'
1352 << par->getCounter(i);
1354 case LABEL_COUNTER_SUBSECTION:
1355 s << par->getCounter(i - 2) << '.'
1356 << par->getCounter(i - 1) << '.'
1357 << par->getCounter(i);
1359 case LABEL_COUNTER_SUBSUBSECTION:
1360 s << par->getCounter(i - 3) << '.'
1361 << par->getCounter(i - 2) << '.'
1362 << par->getCounter(i - 1) << '.'
1363 << par->getCounter(i);
1366 case LABEL_COUNTER_PARAGRAPH:
1367 s << par->getCounter(i - 4) << '.'
1368 << par->getCounter(i - 3) << '.'
1369 << par->getCounter(i - 2) << '.'
1370 << par->getCounter(i - 1) << '.'
1371 << par->getCounter(i);
1373 case LABEL_COUNTER_SUBPARAGRAPH:
1374 s << par->getCounter(i - 5) << '.'
1375 << par->getCounter(i - 4) << '.'
1376 << par->getCounter(i - 3) << '.'
1377 << par->getCounter(i - 2) << '.'
1378 << par->getCounter(i - 1) << '.'
1379 << par->getCounter(i);
1383 // Can this ever be reached? And in the
1384 // case it is, how can this be correct?
1386 s << par->getCounter(i) << '.';
1389 } else { // appendix
1390 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1391 case LABEL_COUNTER_CHAPTER:
1392 if (par->isRightToLeftPar(buf->params))
1393 s << hebrewCounter(par->getCounter(i));
1395 s << alphaCounter(par->getCounter(i));
1397 case LABEL_COUNTER_SECTION:
1398 if (par->isRightToLeftPar(buf->params))
1399 s << hebrewCounter(par->getCounter(i - 1));
1401 s << alphaCounter(par->getCounter(i - 1));
1404 << par->getCounter(i);
1407 case LABEL_COUNTER_SUBSECTION:
1408 if (par->isRightToLeftPar(buf->params))
1409 s << hebrewCounter(par->getCounter(i - 2));
1411 s << alphaCounter(par->getCounter(i - 2));
1414 << par->getCounter(i-1) << '.'
1415 << par->getCounter(i);
1418 case LABEL_COUNTER_SUBSUBSECTION:
1419 if (par->isRightToLeftPar(buf->params))
1420 s << hebrewCounter(par->getCounter(i-3));
1422 s << alphaCounter(par->getCounter(i-3));
1425 << par->getCounter(i-2) << '.'
1426 << par->getCounter(i-1) << '.'
1427 << par->getCounter(i);
1430 case LABEL_COUNTER_PARAGRAPH:
1431 if (par->isRightToLeftPar(buf->params))
1432 s << hebrewCounter(par->getCounter(i-4));
1434 s << alphaCounter(par->getCounter(i-4));
1437 << par->getCounter(i-3) << '.'
1438 << par->getCounter(i-2) << '.'
1439 << par->getCounter(i-1) << '.'
1440 << par->getCounter(i);
1443 case LABEL_COUNTER_SUBPARAGRAPH:
1444 if (par->isRightToLeftPar(buf->params))
1445 s << hebrewCounter(par->getCounter(i-5));
1447 s << alphaCounter(par->getCounter(i-5));
1450 << par->getCounter(i-4) << '.'
1451 << par->getCounter(i-3) << '.'
1452 << par->getCounter(i-2) << '.'
1453 << par->getCounter(i-1) << '.'
1454 << par->getCounter(i);
1458 // Can this ever be reached? And in the
1459 // case it is, how can this be correct?
1461 s << par->getCounter(i) << '.';
1467 par->params().labelString(par->params().labelString() +s.str().c_str());
1468 // We really want to remove the c_str as soon as
1471 for (i++; i < 10; ++i) {
1472 // reset the following counters
1473 par->setCounter(i, 0);
1475 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1476 for (i++; i < 10; ++i) {
1477 // reset the following counters
1478 par->setCounter(i, 0);
1480 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1481 par->incCounter(i + par->enumdepth);
1482 int number = par->getCounter(i + par->enumdepth);
1486 switch (par->enumdepth) {
1488 if (par->isRightToLeftPar(buf->params))
1490 << hebrewCounter(number)
1494 << loweralphaCounter(number)
1498 if (par->isRightToLeftPar(buf->params))
1499 s << '.' << romanCounter(number);
1501 s << romanCounter(number) << '.';
1504 if (par->isRightToLeftPar(buf->params))
1506 << alphaCounter(number);
1508 s << alphaCounter(number)
1512 if (par->isRightToLeftPar(buf->params))
1519 par->params().labelString(s.str().c_str());
1521 for (i += par->enumdepth + 1; i < 10; ++i) {
1522 // reset the following counters
1523 par->setCounter(i, 0);
1527 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1528 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1530 int number = par->getCounter(i);
1532 InsetCommandParams p( "bibitem" );
1533 par->bibkey = new InsetBibKey(p);
1535 par->bibkey->setCounter(number);
1536 par->params().labelString(layout.labelstring());
1538 // In biblio should't be following counters but...
1540 string s = layout.labelstring();
1542 // the caption hack:
1543 if (layout.labeltype == LABEL_SENSITIVE) {
1544 bool isOK (par->inInset() && par->inInset()->owner() &&
1545 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1548 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1550 = floatList.getType(tmp->type());
1551 // We should get the correct number here too.
1552 s = fl.name() + " #:";
1554 /* par->SetLayout(0);
1555 s = layout->labelstring; */
1556 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1557 ? " :úåòîùî øñç" : "Senseless: ";
1560 par->params().labelString(s);
1562 /* reset the enumeration counter. They are always resetted
1563 * when there is any other layout between */
1564 for (int i = 6 + par->enumdepth; i < 10; ++i)
1565 par->setCounter(i, 0);
1570 // Updates all counters BEHIND the row. Changed paragraphs
1571 // with a dynamic left margin will be rebroken.
1572 void LyXText::updateCounters(BufferView * bview, Row * row) const
1580 par = row->par()->next();
1584 while (row->par() != par)
1587 setCounter(bview->buffer(), par);
1589 // now check for the headline layouts. remember that they
1590 // have a dynamic left margin
1591 if ((textclasslist.Style(bview->buffer()->params.textclass,
1592 par->layout).margintype == MARGIN_DYNAMIC
1593 || textclasslist.Style(bview->buffer()->params.textclass,
1594 par->layout).labeltype == LABEL_SENSITIVE)) {
1596 // Rebreak the paragraph
1597 removeParagraph(row);
1598 appendParagraph(bview, row);
1605 void LyXText::insertInset(BufferView * bview, Inset * inset)
1607 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1609 setUndo(bview, Undo::INSERT,
1610 cursor.par(), cursor.par()->next());
1611 cursor.par()->insertInset(cursor.pos(), inset);
1612 // Just to rebreak and refresh correctly.
1613 // The character will not be inserted a second time
1614 insertChar(bview, Paragraph::META_INSET);
1616 // If we enter a highly editable inset the cursor should be to before
1617 // the inset. This couldn't happen before as Undo was not handled inside
1618 // inset now after the Undo LyX tries to call inset->Edit(...) again
1619 // and cannot do this as the cursor is behind the inset and GetInset
1620 // does not return the inset!
1621 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1622 cursorLeft(bview, true);
1628 void LyXText::copyEnvironmentType()
1630 copylayouttype = cursor.par()->getLayout();
1634 void LyXText::pasteEnvironmentType(BufferView * bview)
1636 setLayout(bview, copylayouttype);
1640 void LyXText::cutSelection(BufferView * bview, bool doclear)
1642 // Stuff what we got on the clipboard. Even if there is no selection.
1644 // There is a problem with having the stuffing here in that the
1645 // larger the selection the slower LyX will get. This can be
1646 // solved by running the line below only when the selection has
1647 // finished. The solution used currently just works, to make it
1648 // faster we need to be more clever and probably also have more
1649 // calls to stuffClipboard. (Lgb)
1650 bview->stuffClipboard(selectionAsString(bview->buffer()));
1652 // This doesn't make sense, if there is no selection
1653 if (!selection.set())
1656 // OK, we have a selection. This is always between selection.start
1657 // and selection.end
1659 // make sure that the depth behind the selection are restored, too
1660 Paragraph * endpar = selection.end.par()->next();
1661 Paragraph * undoendpar = endpar;
1663 if (endpar && endpar->getDepth()) {
1664 while (endpar && endpar->getDepth()) {
1665 endpar = endpar->next();
1666 undoendpar = endpar;
1668 } else if (endpar) {
1669 endpar = endpar->next(); // because of parindents etc.
1672 setUndo(bview, Undo::DELETE,
1673 selection.start.par(), undoendpar);
1675 // there are two cases: cut only within one paragraph or
1676 // more than one paragraph
1677 if (selection.start.par() == selection.end.par()) {
1678 // only within one paragraph
1679 endpar = selection.end.par();
1680 int pos = selection.end.pos();
1681 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1682 selection.start.pos(), pos,
1683 bview->buffer()->params.textclass, doclear);
1684 selection.end.pos(pos);
1686 endpar = selection.end.par();
1687 int pos = selection.end.pos();
1688 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1689 selection.start.pos(), pos,
1690 bview->buffer()->params.textclass, doclear);
1692 selection.end.par(endpar);
1693 selection.end.pos(pos);
1694 cursor.pos(selection.end.pos());
1696 endpar = endpar->next();
1698 // sometimes necessary
1700 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1702 redoParagraphs(bview, selection.start, endpar);
1704 // cutSelection can invalidate the cursor so we need to set
1706 cursor = selection.start;
1708 // need a valid cursor. (Lgb)
1709 clearSelection(bview);
1711 setCursor(bview, cursor.par(), cursor.pos());
1712 selection.cursor = cursor;
1713 updateCounters(bview, cursor.row());
1717 void LyXText::copySelection(BufferView * bview)
1719 // Stuff what we got on the clipboard. Even if there is no selection.
1721 // There is a problem with having the stuffing here in that the
1722 // larger the selection the slower LyX will get. This can be
1723 // solved by running the line below only when the selection has
1724 // finished. The solution used currently just works, to make it
1725 // faster we need to be more clever and probably also have more
1726 // calls to stuffClipboard. (Lgb)
1727 bview->stuffClipboard(selectionAsString(bview->buffer()));
1729 // this doesnt make sense, if there is no selection
1730 if (!selection.set())
1733 // ok we have a selection. This is always between selection.start
1734 // and sel_end cursor
1736 // copy behind a space if there is one
1737 while (selection.start.par()->size() > selection.start.pos()
1738 && selection.start.par()->isLineSeparator(selection.start.pos())
1739 && (selection.start.par() != selection.end.par()
1740 || selection.start.pos() < selection.end.pos()))
1741 selection.start.pos(selection.start.pos() + 1);
1743 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1744 selection.start.pos(), selection.end.pos(),
1745 bview->buffer()->params.textclass);
1749 void LyXText::pasteSelection(BufferView * bview)
1751 // this does not make sense, if there is nothing to paste
1752 if (!CutAndPaste::checkPastePossible(cursor.par()))
1755 setUndo(bview, Undo::INSERT,
1756 cursor.par(), cursor.par()->next());
1759 Paragraph * actpar = cursor.par();
1761 int pos = cursor.pos();
1762 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1763 bview->buffer()->params.textclass);
1765 redoParagraphs(bview, cursor, endpar);
1767 setCursor(bview, cursor.par(), cursor.pos());
1768 clearSelection(bview);
1770 selection.cursor = cursor;
1771 setCursor(bview, actpar, pos);
1772 setSelection(bview);
1773 updateCounters(bview, cursor.row());
1777 // returns a pointer to the very first Paragraph
1778 Paragraph * LyXText::firstParagraph() const
1780 return ownerParagraph();
1784 // sets the selection over the number of characters of string, no check!!
1785 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1790 selection.cursor = cursor;
1791 for (string::size_type i = 0; i < str.length(); ++i)
1793 setSelection(bview);
1797 // simple replacing. The font of the first selected character is used
1798 void LyXText::replaceSelectionWithString(BufferView * bview,
1801 setCursorParUndo(bview);
1804 if (!selection.set()) { // create a dummy selection
1805 selection.end = cursor;
1806 selection.start = cursor;
1809 // Get font setting before we cut
1810 Paragraph::size_type pos = selection.end.pos();
1811 LyXFont const font = selection.start.par()
1812 ->getFontSettings(bview->buffer()->params,
1813 selection.start.pos());
1815 // Insert the new string
1816 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1817 selection.end.par()->insertChar(pos, (*cit), font);
1821 // Cut the selection
1822 cutSelection(bview);
1828 // needed to insert the selection
1829 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1831 Paragraph * par = cursor.par();
1832 Paragraph::size_type pos = cursor.pos();
1833 Paragraph * endpar = cursor.par()->next();
1835 setCursorParUndo(bview);
1837 // only to be sure, should not be neccessary
1838 clearSelection(bview);
1840 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1842 redoParagraphs(bview, cursor, endpar);
1843 setCursor(bview, cursor.par(), cursor.pos());
1844 selection.cursor = cursor;
1845 setCursor(bview, par, pos);
1846 setSelection(bview);
1850 // turns double-CR to single CR, others where converted into one
1851 // blank. Then InsertStringAsLines is called
1852 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1854 string linestr(str);
1855 bool newline_inserted = false;
1856 for (string::size_type i = 0; i < linestr.length(); ++i) {
1857 if (linestr[i] == '\n') {
1858 if (newline_inserted) {
1859 // we know that \r will be ignored by
1860 // InsertStringA. Of course, it is a dirty
1861 // trick, but it works...
1862 linestr[i - 1] = '\r';
1866 newline_inserted = true;
1868 } else if (IsPrintable(linestr[i])) {
1869 newline_inserted = false;
1872 insertStringAsLines(bview, linestr);
1876 bool LyXText::gotoNextInset(BufferView * bview,
1877 std::vector<Inset::Code> const & codes,
1878 string const & contents) const
1880 LyXCursor res = cursor;
1883 if (res.pos() < res.par()->size() - 1) {
1884 res.pos(res.pos() + 1);
1886 res.par(res.par()->next());
1890 } while (res.par() &&
1891 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1892 && (inset = res.par()->getInset(res.pos())) != 0
1893 && find(codes.begin(), codes.end(), inset->lyxCode())
1895 && (contents.empty() ||
1896 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1900 setCursor(bview, res.par(), res.pos());
1907 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1908 Paragraph::size_type pos)
1910 LyXCursor tmpcursor;
1913 Paragraph::size_type z;
1914 Row * row = getRow(par, pos, y);
1916 // is there a break one row above
1917 if (row->previous() && row->previous()->par() == row->par()) {
1918 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1919 if (z >= row->pos()) {
1920 // set the dimensions of the row above
1921 y -= row->previous()->height();
1923 refresh_row = row->previous();
1924 status(bview, LyXText::NEED_MORE_REFRESH);
1926 breakAgain(bview, row->previous());
1928 // set the cursor again. Otherwise
1929 // dangling pointers are possible
1930 setCursor(bview, cursor.par(), cursor.pos(),
1931 false, cursor.boundary());
1932 selection.cursor = cursor;
1937 int const tmpheight = row->height();
1938 Paragraph::size_type const tmplast = rowLast(row);
1942 breakAgain(bview, row);
1943 if (row->height() == tmpheight && rowLast(row) == tmplast)
1944 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1946 status(bview, LyXText::NEED_MORE_REFRESH);
1948 // check the special right address boxes
1949 if (textclasslist.Style(bview->buffer()->params.textclass,
1950 par->getLayout()).margintype
1951 == MARGIN_RIGHT_ADDRESS_BOX) {
1958 redoDrawingOfParagraph(bview, tmpcursor);
1961 // set the cursor again. Otherwise dangling pointers are possible
1962 // also set the selection
1964 if (selection.set()) {
1966 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1967 false, selection.cursor.boundary());
1968 selection.cursor = cursor;
1969 setCursorIntern(bview, selection.start.par(),
1970 selection.start.pos(),
1971 false, selection.start.boundary());
1972 selection.start = cursor;
1973 setCursorIntern(bview, selection.end.par(),
1974 selection.end.pos(),
1975 false, selection.end.boundary());
1976 selection.end = cursor;
1977 setCursorIntern(bview, last_sel_cursor.par(),
1978 last_sel_cursor.pos(),
1979 false, last_sel_cursor.boundary());
1980 last_sel_cursor = cursor;
1983 setCursorIntern(bview, cursor.par(), cursor.pos(),
1984 false, cursor.boundary());
1988 // returns false if inset wasn't found
1989 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1991 // first check the current paragraph
1992 int pos = cursor.par()->getPositionOfInset(inset);
1994 checkParagraph(bview, cursor.par(), pos);
1998 // check every paragraph
2000 Paragraph * par = firstParagraph();
2002 pos = par->getPositionOfInset(inset);
2004 checkParagraph(bview, par, pos);
2014 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2015 Paragraph::size_type pos,
2016 bool setfont, bool boundary) const
2018 LyXCursor old_cursor = cursor;
2019 setCursorIntern(bview, par, pos, setfont, boundary);
2020 deleteEmptyParagraphMechanism(bview, old_cursor);
2024 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2025 Paragraph::size_type pos, bool boundary) const
2029 cur.boundary(boundary);
2031 // get the cursor y position in text
2033 Row * row = getRow(par, pos, y);
2034 // y is now the beginning of the cursor row
2035 y += row->baseline();
2036 // y is now the cursor baseline
2039 // now get the cursors x position
2041 float fill_separator;
2043 float fill_label_hfill;
2044 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2046 Paragraph::size_type cursor_vpos = 0;
2047 Paragraph::size_type last = rowLastPrintable(row);
2049 if (pos > last + 1) // This shouldn't happen.
2051 else if (pos < row->pos())
2054 if (last < row->pos())
2055 cursor_vpos = row->pos();
2056 else if (pos > last && !boundary)
2057 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2058 ? row->pos() : last + 1;
2059 else if (pos > row->pos() &&
2060 (pos > last || boundary))
2061 /// Place cursor after char at (logical) position pos - 1
2062 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2063 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2065 /// Place cursor before char at (logical) position pos
2066 cursor_vpos = (bidi_level(pos) % 2 == 0)
2067 ? log2vis(pos) : log2vis(pos) + 1;
2069 Paragraph::size_type main_body =
2070 beginningOfMainBody(bview->buffer(), row->par());
2071 if ((main_body > 0) &&
2072 ((main_body-1 > last) ||
2073 !row->par()->isLineSeparator(main_body-1)))
2076 for (Paragraph::size_type vpos = row->pos();
2077 vpos < cursor_vpos; ++vpos) {
2078 pos = vis2log(vpos);
2079 if (main_body > 0 && pos == main_body - 1) {
2080 x += fill_label_hfill +
2081 lyxfont::width(textclasslist.Style(
2082 bview->buffer()->params.textclass,
2083 row->par()->getLayout())
2085 getFont(bview->buffer(), row->par(), -2));
2086 if (row->par()->isLineSeparator(main_body-1))
2087 x -= singleWidth(bview, row->par(),main_body-1);
2089 if (hfillExpansion(bview->buffer(), row, pos)) {
2090 x += singleWidth(bview, row->par(), pos);
2091 if (pos >= main_body)
2094 x += fill_label_hfill;
2095 } else if (row->par()->isSeparator(pos)) {
2096 x += singleWidth(bview, row->par(), pos);
2097 if (pos >= main_body)
2098 x += fill_separator;
2100 x += singleWidth(bview, row->par(), pos);
2109 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2110 Paragraph::size_type pos,
2111 bool setfont, bool boundary) const
2113 InsetText * it = static_cast<InsetText *>(par->inInset());
2114 if (it && (it != inset_owner)) {
2115 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2118 setCursor(bview, cursor, par, pos, boundary);
2120 setCurrentFont(bview);
2125 void LyXText::setCurrentFont(BufferView * bview) const
2127 Paragraph::size_type pos = cursor.pos();
2128 if (cursor.boundary() && pos > 0)
2132 if (pos == cursor.par()->size())
2134 else // potentional bug... BUG (Lgb)
2135 if (cursor.par()->isSeparator(pos)) {
2136 if (pos > cursor.row()->pos() &&
2137 bidi_level(pos) % 2 ==
2138 bidi_level(pos - 1) % 2)
2140 else if (pos + 1 < cursor.par()->size())
2146 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2147 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2149 if (cursor.pos() == cursor.par()->size() &&
2150 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2151 !cursor.boundary()) {
2152 Language const * lang =
2153 cursor.par()->getParLanguage(bview->buffer()->params);
2154 current_font.setLanguage(lang);
2155 current_font.setNumber(LyXFont::OFF);
2156 real_current_font.setLanguage(lang);
2157 real_current_font.setNumber(LyXFont::OFF);
2162 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2164 LyXCursor old_cursor = cursor;
2166 // Get the row first.
2168 Row * row = getRowNearY(y);
2169 cursor.par(row->par());
2172 int column = getColumnNearX(bview, row, x, bound);
2173 cursor.pos(row->pos() + column);
2175 cursor.y(y + row->baseline());
2177 cursor.boundary(bound);
2178 setCurrentFont(bview);
2179 deleteEmptyParagraphMechanism(bview, old_cursor);
2183 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2186 // Get the row first.
2188 Row * row = getRowNearY(y);
2190 int column = getColumnNearX(bview, row, x, bound);
2192 cur.par(row->par());
2193 cur.pos(row->pos() + column);
2195 cur.y(y + row->baseline());
2197 cur.boundary(bound);
2201 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2203 if (cursor.pos() > 0) {
2204 bool boundary = cursor.boundary();
2205 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2206 if (!internal && !boundary &&
2207 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2208 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2209 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2210 Paragraph * par = cursor.par()->previous();
2211 setCursor(bview, par, par->size());
2216 void LyXText::cursorRight(BufferView * bview, bool internal) const
2218 if (!internal && cursor.boundary() &&
2219 !cursor.par()->isNewline(cursor.pos()))
2220 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2221 else if (cursor.pos() < cursor.par()->size()) {
2222 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2224 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2225 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2226 } else if (cursor.par()->next())
2227 setCursor(bview, cursor.par()->next(), 0);
2231 void LyXText::cursorUp(BufferView * bview) const
2233 setCursorFromCoordinates(bview, cursor.x_fix(),
2234 cursor.y() - cursor.row()->baseline() - 1);
2238 void LyXText::cursorDown(BufferView * bview) const
2240 setCursorFromCoordinates(bview, cursor.x_fix(),
2241 cursor.y() - cursor.row()->baseline()
2242 + cursor.row()->height() + 1);
2246 void LyXText::cursorUpParagraph(BufferView * bview) const
2248 if (cursor.pos() > 0) {
2249 setCursor(bview, cursor.par(), 0);
2251 else if (cursor.par()->previous()) {
2252 setCursor(bview, cursor.par()->previous(), 0);
2257 void LyXText::cursorDownParagraph(BufferView * bview) const
2259 if (cursor.par()->next()) {
2260 setCursor(bview, cursor.par()->next(), 0);
2262 setCursor(bview, cursor.par(), cursor.par()->size());
2267 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2268 LyXCursor const & old_cursor) const
2270 // Would be wrong to delete anything if we have a selection.
2271 if (selection.set()) return;
2273 // We allow all kinds of "mumbo-jumbo" when freespacing.
2274 if (textclasslist.Style(bview->buffer()->params.textclass,
2275 old_cursor.par()->getLayout()).free_spacing)
2278 bool deleted = false;
2280 /* Ok I'll put some comments here about what is missing.
2281 I have fixed BackSpace (and thus Delete) to not delete
2282 double-spaces automagically. I have also changed Cut,
2283 Copy and Paste to hopefully do some sensible things.
2284 There are still some small problems that can lead to
2285 double spaces stored in the document file or space at
2286 the beginning of paragraphs. This happens if you have
2287 the cursor betwenn to spaces and then save. Or if you
2288 cut and paste and the selection have a space at the
2289 beginning and then save right after the paste. I am
2290 sure none of these are very hard to fix, but I will
2291 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2292 that I can get some feedback. (Lgb)
2295 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2296 // delete the LineSeparator.
2299 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2300 // delete the LineSeparator.
2303 // If the pos around the old_cursor were spaces, delete one of them.
2304 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2305 // Only if the cursor has really moved
2307 if (old_cursor.pos() > 0
2308 && old_cursor.pos() < old_cursor.par()->size()
2309 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2310 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2311 old_cursor.par()->erase(old_cursor.pos() - 1);
2312 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2314 if (old_cursor.par() == cursor.par() &&
2315 cursor.pos() > old_cursor.pos()) {
2316 setCursorIntern(bview, cursor.par(),
2319 setCursorIntern(bview, cursor.par(),
2325 // Do not delete empty paragraphs with keepempty set.
2326 if ((textclasslist.Style(bview->buffer()->params.textclass,
2327 old_cursor.par()->getLayout())).keepempty)
2330 LyXCursor tmpcursor;
2332 if (old_cursor.par() != cursor.par()) {
2333 if ((old_cursor.par()->size() == 0
2334 || (old_cursor.par()->size() == 1
2335 && old_cursor.par()->isLineSeparator(0)))) {
2336 // ok, we will delete anything
2338 // make sure that you do not delete any environments
2339 status(bview, LyXText::NEED_MORE_REFRESH);
2342 if (old_cursor.row()->previous()) {
2343 refresh_row = old_cursor.row()->previous();
2344 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2346 cursor = old_cursor; // that undo can restore the right cursor position
2347 Paragraph * endpar = old_cursor.par()->next();
2348 if (endpar && endpar->getDepth()) {
2349 while (endpar && endpar->getDepth()) {
2350 endpar = endpar->next();
2353 setUndo(bview, Undo::DELETE,
2359 removeRow(old_cursor.row());
2360 if (ownerParagraph() == old_cursor.par()) {
2361 ownerParagraph(ownerParagraph()->next());
2364 delete old_cursor.par();
2366 /* Breakagain the next par. Needed
2367 * because of the parindent that
2368 * can occur or dissappear. The
2369 * next row can change its height,
2370 * if there is another layout before */
2371 if (refresh_row->next()) {
2372 breakAgain(bview, refresh_row->next());
2373 updateCounters(bview, refresh_row);
2375 setHeightOfRow(bview, refresh_row);
2377 refresh_row = old_cursor.row()->next();
2378 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2381 cursor = old_cursor; // that undo can restore the right cursor position
2382 Paragraph * endpar = old_cursor.par()->next();
2383 if (endpar && endpar->getDepth()) {
2384 while (endpar && endpar->getDepth()) {
2385 endpar = endpar->next();
2388 setUndo(bview, Undo::DELETE,
2394 removeRow(old_cursor.row());
2396 if (ownerParagraph() == old_cursor.par()) {
2397 ownerParagraph(ownerParagraph()->next());
2400 delete old_cursor.par();
2402 /* Breakagain the next par. Needed
2403 because of the parindent that can
2404 occur or dissappear.
2405 The next row can change its height,
2406 if there is another layout before
2409 breakAgain(bview, refresh_row);
2410 updateCounters(bview, refresh_row->previous());
2416 setCursorIntern(bview, cursor.par(), cursor.pos());
2418 if (selection.cursor.par() == old_cursor.par()
2419 && selection.cursor.pos() == selection.cursor.pos()) {
2420 // correct selection
2421 selection.cursor = cursor;
2425 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2426 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2428 setCursorIntern(bview, cursor.par(), cursor.pos());
2429 selection.cursor = cursor;
2436 void LyXText::toggleAppendix(BufferView * bview)
2438 Paragraph * par = cursor.par();
2439 bool start = !par->params().startOfAppendix();
2441 // ensure that we have only one start_of_appendix in this document
2442 Paragraph * tmp = firstParagraph();
2443 for (; tmp; tmp = tmp->next()) {
2444 tmp->params().startOfAppendix(false);
2447 par->params().startOfAppendix(start);
2449 // we can set the refreshing parameters now
2450 status(bview, LyXText::NEED_MORE_REFRESH);
2452 refresh_row = 0; // not needed for full update
2453 updateCounters(bview, 0);
2454 setCursor(bview, cursor.par(), cursor.pos());
2458 Paragraph * LyXText::ownerParagraph() const
2461 return inset_owner->paragraph();
2463 return bv_owner->buffer()->paragraph;
2467 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2470 inset_owner->paragraph(p);
2472 bv_owner->buffer()->paragraph = p;
2477 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2479 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2480 if (op && op->inInset()) {
2481 static_cast<InsetText *>(op->inInset())->paragraph(p);
2484 inset_owner->paragraph(p);
2486 bv_owner->buffer()->paragraph = p;
2493 LyXText::text_status LyXText::status() const
2499 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2502 if ((status_ != NEED_MORE_REFRESH)
2503 || (status_ == NEED_MORE_REFRESH)
2504 && (st != NEED_VERY_LITTLE_REFRESH)) {
2506 if (inset_owner && st != UNCHANGED) {
2507 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2511 #warning Please tell what the intention is here. (Lgb)
2512 // The above does not make any sense, I changed it to what is here,
2513 // but it still does not make much sense. (Lgb)
2514 #warning Sure have a look now! (Jug)
2515 // well as much as I know && binds more then || so the above and the
2516 // below are identical (this for your known use of parentesis!)
2517 // Now some explanation:
2518 // We should only go up with refreshing code so this means that if
2519 // we have a MORE refresh we should never set it to LITTLE if we still
2520 // didn't handle it (and then it will be UNCHANGED. Now as long as
2521 // we stay inside one LyXText this may work but we need to tell the
2522 // outermost LyXText that it should REALLY draw us if there is some
2523 // change in a Inset::LyXText. So you see that when we are inside a
2524 // inset's LyXText we give the LITTLE to the outermost LyXText to
2525 // tell'em that it should redraw the actual row (where the inset
2526 // resides! Capito?!
2528 if ((status_ != NEED_MORE_REFRESH)
2529 || (status_ == NEED_MORE_REFRESH
2530 && st != NEED_VERY_LITTLE_REFRESH))
2533 if (inset_owner && st != UNCHANGED) {
2534 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);