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);
1062 Paragraph::size_type
1063 LyXText::beginningOfMainBody(Buffer const * buf,
1064 Paragraph const * par) const
1066 if (textclasslist.Style(buf->params.textclass,
1067 par->getLayout()).labeltype != LABEL_MANUAL)
1070 return par->beginningOfMainBody();
1074 /* the DTP switches for paragraphs. LyX will store them in the
1075 * first physicla paragraph. When a paragraph is broken, the top settings
1076 * rest, the bottom settings are given to the new one. So I can make shure,
1077 * they do not duplicate themself and you cannnot make dirty things with
1080 void LyXText::setParagraph(BufferView * bview,
1081 bool line_top, bool line_bottom,
1082 bool pagebreak_top, bool pagebreak_bottom,
1083 VSpace const & space_top,
1084 VSpace const & space_bottom,
1086 string labelwidthstring,
1089 LyXCursor tmpcursor = cursor;
1090 if (!selection.set()) {
1091 selection.start = cursor;
1092 selection.end = cursor;
1095 // make sure that the depth behind the selection are restored, too
1096 Paragraph * endpar = selection.end.par()->next();
1097 Paragraph * undoendpar = endpar;
1099 if (endpar && endpar->getDepth()) {
1100 while (endpar && endpar->getDepth()) {
1101 endpar = endpar->next();
1102 undoendpar = endpar;
1106 // because of parindents etc.
1107 endpar = endpar->next();
1110 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1113 Paragraph * tmppar = selection.end.par();
1114 while (tmppar != selection.start.par()->previous()) {
1115 setCursor(bview, tmppar, 0);
1116 status(bview, LyXText::NEED_MORE_REFRESH);
1117 refresh_row = cursor.row();
1118 refresh_y = cursor.y() - cursor.row()->baseline();
1119 cursor.par()->params().lineTop(line_top);
1120 cursor.par()->params().lineBottom(line_bottom);
1121 cursor.par()->params().pagebreakTop(pagebreak_top);
1122 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1123 cursor.par()->params().spaceTop(space_top);
1124 cursor.par()->params().spaceBottom(space_bottom);
1125 // does the layout allow the new alignment?
1126 if (align == LYX_ALIGN_LAYOUT)
1127 align = textclasslist
1128 .Style(bview->buffer()->params.textclass,
1129 cursor.par()->getLayout()).align;
1130 if (align & textclasslist
1131 .Style(bview->buffer()->params.textclass,
1132 cursor.par()->getLayout()).alignpossible) {
1133 if (align == textclasslist
1134 .Style(bview->buffer()->params.textclass,
1135 cursor.par()->getLayout()).align)
1136 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1138 cursor.par()->params().align(align);
1140 cursor.par()->setLabelWidthString(labelwidthstring);
1141 cursor.par()->params().noindent(noindent);
1142 tmppar = cursor.par()->previous();
1145 redoParagraphs(bview, selection.start, endpar);
1147 clearSelection(bview);
1148 setCursor(bview, selection.start.par(), selection.start.pos());
1149 selection.cursor = cursor;
1150 setCursor(bview, selection.end.par(), selection.end.pos());
1151 setSelection(bview);
1152 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1154 bview->updateInset(inset_owner, true);
1158 char loweralphaCounter(int n)
1160 if (n < 1 || n > 26)
1170 char alphaCounter(int n)
1172 if (n < 1 || n > 26)
1180 char hebrewCounter(int n)
1182 static const char hebrew[22] = {
1183 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1184 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1187 if (n < 1 || n > 22)
1195 string const romanCounter(int n)
1197 static char const * roman[20] = {
1198 "i", "ii", "iii", "iv", "v",
1199 "vi", "vii", "viii", "ix", "x",
1200 "xi", "xii", "xiii", "xiv", "xv",
1201 "xvi", "xvii", "xviii", "xix", "xx"
1203 if (n < 1 || n > 20)
1212 // set the counter of a paragraph. This includes the labels
1213 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1215 LyXLayout const & layout =
1216 textclasslist.Style(buf->params.textclass,
1219 LyXTextClass const & textclass =
1220 textclasslist.TextClass(buf->params.textclass);
1222 // copy the prev-counters to this one,
1223 // unless this is the first paragraph
1224 if (par->previous()) {
1225 for (int i = 0; i < 10; ++i) {
1226 par->setCounter(i, par->previous()->getFirstCounter(i));
1228 par->params().appendix(par->previous()->params().appendix());
1229 if (!par->params().appendix() && par->params().startOfAppendix()) {
1230 par->params().appendix(true);
1231 for (int i = 0; i < 10; ++i) {
1232 par->setCounter(i, 0);
1235 par->enumdepth = par->previous()->enumdepth;
1236 par->itemdepth = par->previous()->itemdepth;
1238 for (int i = 0; i < 10; ++i) {
1239 par->setCounter(i, 0);
1241 par->params().appendix(par->params().startOfAppendix());
1246 /* Maybe we have to increment the enumeration depth.
1247 * BUT, enumeration in a footnote is considered in isolation from its
1248 * surrounding paragraph so don't increment if this is the
1249 * first line of the footnote
1250 * AND, bibliographies can't have their depth changed ie. they
1251 * are always of depth 0
1254 && par->previous()->getDepth() < par->getDepth()
1255 && textclasslist.Style(buf->params.textclass,
1256 par->previous()->getLayout()
1257 ).labeltype == LABEL_COUNTER_ENUMI
1258 && par->enumdepth < 3
1259 && layout.labeltype != LABEL_BIBLIO) {
1263 // Maybe we have to decrement the enumeration depth, see note above
1265 && par->previous()->getDepth() > par->getDepth()
1266 && layout.labeltype != LABEL_BIBLIO) {
1267 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1268 par->setCounter(6 + par->enumdepth,
1269 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1270 /* reset the counters.
1271 * A depth change is like a breaking layout
1273 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1274 par->setCounter(i, 0);
1277 if (!par->params().labelString().empty()) {
1278 par->params().labelString(string());
1281 if (layout.margintype == MARGIN_MANUAL) {
1282 if (par->params().labelWidthString().empty()) {
1283 par->setLabelWidthString(layout.labelstring());
1286 par->setLabelWidthString(string());
1289 // is it a layout that has an automatic label?
1290 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1292 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1293 if (i >= 0 && i<= buf->params.secnumdepth) {
1294 par->incCounter(i); // increment the counter
1296 // Is there a label? Useful for Chapter layout
1297 if (!par->params().appendix()) {
1298 if (!layout.labelstring().empty())
1299 par->params().labelString(layout.labelstring());
1301 par->params().labelString(string());
1303 if (!layout.labelstring_appendix().empty())
1304 par->params().labelString(layout.labelstring_appendix());
1306 par->params().labelString(string());
1311 if (!par->params().appendix()) {
1312 switch (2 * LABEL_COUNTER_CHAPTER -
1313 textclass.maxcounter() + i) {
1314 case LABEL_COUNTER_CHAPTER:
1315 s << par->getCounter(i);
1317 case LABEL_COUNTER_SECTION:
1318 s << par->getCounter(i - 1) << '.'
1319 << par->getCounter(i);
1321 case LABEL_COUNTER_SUBSECTION:
1322 s << par->getCounter(i - 2) << '.'
1323 << par->getCounter(i - 1) << '.'
1324 << par->getCounter(i);
1326 case LABEL_COUNTER_SUBSUBSECTION:
1327 s << par->getCounter(i - 3) << '.'
1328 << par->getCounter(i - 2) << '.'
1329 << par->getCounter(i - 1) << '.'
1330 << par->getCounter(i);
1333 case LABEL_COUNTER_PARAGRAPH:
1334 s << par->getCounter(i - 4) << '.'
1335 << par->getCounter(i - 3) << '.'
1336 << par->getCounter(i - 2) << '.'
1337 << par->getCounter(i - 1) << '.'
1338 << par->getCounter(i);
1340 case LABEL_COUNTER_SUBPARAGRAPH:
1341 s << par->getCounter(i - 5) << '.'
1342 << par->getCounter(i - 4) << '.'
1343 << par->getCounter(i - 3) << '.'
1344 << par->getCounter(i - 2) << '.'
1345 << par->getCounter(i - 1) << '.'
1346 << par->getCounter(i);
1350 // Can this ever be reached? And in the
1351 // case it is, how can this be correct?
1353 s << par->getCounter(i) << '.';
1356 } else { // appendix
1357 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1358 case LABEL_COUNTER_CHAPTER:
1359 if (par->isRightToLeftPar(buf->params))
1360 s << hebrewCounter(par->getCounter(i));
1362 s << alphaCounter(par->getCounter(i));
1364 case LABEL_COUNTER_SECTION:
1365 if (par->isRightToLeftPar(buf->params))
1366 s << hebrewCounter(par->getCounter(i - 1));
1368 s << alphaCounter(par->getCounter(i - 1));
1371 << par->getCounter(i);
1374 case LABEL_COUNTER_SUBSECTION:
1375 if (par->isRightToLeftPar(buf->params))
1376 s << hebrewCounter(par->getCounter(i - 2));
1378 s << alphaCounter(par->getCounter(i - 2));
1381 << par->getCounter(i-1) << '.'
1382 << par->getCounter(i);
1385 case LABEL_COUNTER_SUBSUBSECTION:
1386 if (par->isRightToLeftPar(buf->params))
1387 s << hebrewCounter(par->getCounter(i-3));
1389 s << alphaCounter(par->getCounter(i-3));
1392 << par->getCounter(i-2) << '.'
1393 << par->getCounter(i-1) << '.'
1394 << par->getCounter(i);
1397 case LABEL_COUNTER_PARAGRAPH:
1398 if (par->isRightToLeftPar(buf->params))
1399 s << hebrewCounter(par->getCounter(i-4));
1401 s << alphaCounter(par->getCounter(i-4));
1404 << par->getCounter(i-3) << '.'
1405 << par->getCounter(i-2) << '.'
1406 << par->getCounter(i-1) << '.'
1407 << par->getCounter(i);
1410 case LABEL_COUNTER_SUBPARAGRAPH:
1411 if (par->isRightToLeftPar(buf->params))
1412 s << hebrewCounter(par->getCounter(i-5));
1414 s << alphaCounter(par->getCounter(i-5));
1417 << par->getCounter(i-4) << '.'
1418 << par->getCounter(i-3) << '.'
1419 << par->getCounter(i-2) << '.'
1420 << par->getCounter(i-1) << '.'
1421 << par->getCounter(i);
1425 // Can this ever be reached? And in the
1426 // case it is, how can this be correct?
1428 s << par->getCounter(i) << '.';
1434 par->params().labelString(par->params().labelString() +s.str().c_str());
1435 // We really want to remove the c_str as soon as
1438 for (i++; i < 10; ++i) {
1439 // reset the following counters
1440 par->setCounter(i, 0);
1442 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1443 for (i++; i < 10; ++i) {
1444 // reset the following counters
1445 par->setCounter(i, 0);
1447 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1448 par->incCounter(i + par->enumdepth);
1449 int number = par->getCounter(i + par->enumdepth);
1453 switch (par->enumdepth) {
1455 if (par->isRightToLeftPar(buf->params))
1457 << hebrewCounter(number)
1461 << loweralphaCounter(number)
1465 if (par->isRightToLeftPar(buf->params))
1466 s << '.' << romanCounter(number);
1468 s << romanCounter(number) << '.';
1471 if (par->isRightToLeftPar(buf->params))
1473 << alphaCounter(number);
1475 s << alphaCounter(number)
1479 if (par->isRightToLeftPar(buf->params))
1486 par->params().labelString(s.str().c_str());
1488 for (i += par->enumdepth + 1; i < 10; ++i) {
1489 // reset the following counters
1490 par->setCounter(i, 0);
1494 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1495 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1497 int number = par->getCounter(i);
1499 InsetCommandParams p( "bibitem" );
1500 par->bibkey = new InsetBibKey(p);
1502 par->bibkey->setCounter(number);
1503 par->params().labelString(layout.labelstring());
1505 // In biblio should't be following counters but...
1507 string s = layout.labelstring();
1509 // the caption hack:
1510 if (layout.labeltype == LABEL_SENSITIVE) {
1511 bool isOK (par->inInset() && par->inInset()->owner() &&
1512 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1515 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1517 = floatList.getType(tmp->type());
1518 // We should get the correct number here too.
1519 s = fl.name() + " #:";
1521 /* par->SetLayout(0);
1522 s = layout->labelstring; */
1523 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1524 ? " :úåòîùî øñç" : "Senseless: ";
1527 par->params().labelString(s);
1529 /* reset the enumeration counter. They are always resetted
1530 * when there is any other layout between */
1531 for (int i = 6 + par->enumdepth; i < 10; ++i)
1532 par->setCounter(i, 0);
1537 // Updates all counters BEHIND the row. Changed paragraphs
1538 // with a dynamic left margin will be rebroken.
1539 void LyXText::updateCounters(BufferView * bview, Row * row) const
1547 par = row->par()->next();
1551 while (row->par() != par)
1554 setCounter(bview->buffer(), par);
1556 // now check for the headline layouts. remember that they
1557 // have a dynamic left margin
1558 if ((textclasslist.Style(bview->buffer()->params.textclass,
1559 par->layout).margintype == MARGIN_DYNAMIC
1560 || textclasslist.Style(bview->buffer()->params.textclass,
1561 par->layout).labeltype == LABEL_SENSITIVE)) {
1563 // Rebreak the paragraph
1564 removeParagraph(row);
1565 appendParagraph(bview, row);
1572 void LyXText::insertInset(BufferView * bview, Inset * inset)
1574 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1576 setUndo(bview, Undo::INSERT,
1577 cursor.par(), cursor.par()->next());
1578 cursor.par()->insertInset(cursor.pos(), inset);
1579 // Just to rebreak and refresh correctly.
1580 // The character will not be inserted a second time
1581 insertChar(bview, Paragraph::META_INSET);
1583 // If we enter a highly editable inset the cursor should be to before
1584 // the inset. This couldn't happen before as Undo was not handled inside
1585 // inset now after the Undo LyX tries to call inset->Edit(...) again
1586 // and cannot do this as the cursor is behind the inset and GetInset
1587 // does not return the inset!
1588 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1589 cursorLeft(bview, true);
1595 void LyXText::copyEnvironmentType()
1597 copylayouttype = cursor.par()->getLayout();
1601 void LyXText::pasteEnvironmentType(BufferView * bview)
1603 setLayout(bview, copylayouttype);
1607 void LyXText::cutSelection(BufferView * bview, bool doclear)
1609 // Stuff what we got on the clipboard. Even if there is no selection.
1611 // There is a problem with having the stuffing here in that the
1612 // larger the selection the slower LyX will get. This can be
1613 // solved by running the line below only when the selection has
1614 // finished. The solution used currently just works, to make it
1615 // faster we need to be more clever and probably also have more
1616 // calls to stuffClipboard. (Lgb)
1617 bview->stuffClipboard(selectionAsString(bview->buffer()));
1619 // This doesn't make sense, if there is no selection
1620 if (!selection.set())
1623 // OK, we have a selection. This is always between selection.start
1624 // and selection.end
1626 // make sure that the depth behind the selection are restored, too
1627 Paragraph * endpar = selection.end.par()->next();
1628 Paragraph * undoendpar = endpar;
1630 if (endpar && endpar->getDepth()) {
1631 while (endpar && endpar->getDepth()) {
1632 endpar = endpar->next();
1633 undoendpar = endpar;
1635 } else if (endpar) {
1636 endpar = endpar->next(); // because of parindents etc.
1639 setUndo(bview, Undo::DELETE,
1640 selection.start.par(), undoendpar);
1642 // there are two cases: cut only within one paragraph or
1643 // more than one paragraph
1644 if (selection.start.par() == selection.end.par()) {
1645 // only within one paragraph
1646 endpar = selection.end.par();
1647 int pos = selection.end.pos();
1648 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1649 selection.start.pos(), pos,
1650 bview->buffer()->params.textclass, doclear);
1651 selection.end.pos(pos);
1653 endpar = selection.end.par();
1654 int pos = selection.end.pos();
1655 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1656 selection.start.pos(), pos,
1657 bview->buffer()->params.textclass, doclear);
1659 selection.end.par(endpar);
1660 selection.end.pos(pos);
1661 cursor.pos(selection.end.pos());
1663 endpar = endpar->next();
1665 // sometimes necessary
1667 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1669 redoParagraphs(bview, selection.start, endpar);
1671 // cutSelection can invalidate the cursor so we need to set
1673 cursor = selection.start;
1675 // need a valid cursor. (Lgb)
1676 clearSelection(bview);
1678 setCursor(bview, cursor.par(), cursor.pos());
1679 selection.cursor = cursor;
1680 updateCounters(bview, cursor.row());
1684 void LyXText::copySelection(BufferView * bview)
1686 // Stuff what we got on the clipboard. Even if there is no selection.
1688 // There is a problem with having the stuffing here in that the
1689 // larger the selection the slower LyX will get. This can be
1690 // solved by running the line below only when the selection has
1691 // finished. The solution used currently just works, to make it
1692 // faster we need to be more clever and probably also have more
1693 // calls to stuffClipboard. (Lgb)
1694 bview->stuffClipboard(selectionAsString(bview->buffer()));
1696 // this doesnt make sense, if there is no selection
1697 if (!selection.set())
1700 // ok we have a selection. This is always between selection.start
1701 // and sel_end cursor
1703 // copy behind a space if there is one
1704 while (selection.start.par()->size() > selection.start.pos()
1705 && selection.start.par()->isLineSeparator(selection.start.pos())
1706 && (selection.start.par() != selection.end.par()
1707 || selection.start.pos() < selection.end.pos()))
1708 selection.start.pos(selection.start.pos() + 1);
1710 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1711 selection.start.pos(), selection.end.pos(),
1712 bview->buffer()->params.textclass);
1716 void LyXText::pasteSelection(BufferView * bview)
1718 // this does not make sense, if there is nothing to paste
1719 if (!CutAndPaste::checkPastePossible(cursor.par()))
1722 setUndo(bview, Undo::INSERT,
1723 cursor.par(), cursor.par()->next());
1726 Paragraph * actpar = cursor.par();
1728 int pos = cursor.pos();
1729 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1730 bview->buffer()->params.textclass);
1732 redoParagraphs(bview, cursor, endpar);
1734 setCursor(bview, cursor.par(), cursor.pos());
1735 clearSelection(bview);
1737 selection.cursor = cursor;
1738 setCursor(bview, actpar, pos);
1739 setSelection(bview);
1740 updateCounters(bview, cursor.row());
1744 // returns a pointer to the very first Paragraph
1745 Paragraph * LyXText::firstParagraph() const
1747 return ownerParagraph();
1751 // sets the selection over the number of characters of string, no check!!
1752 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1757 selection.cursor = cursor;
1758 for (string::size_type i = 0; i < str.length(); ++i)
1760 setSelection(bview);
1764 // simple replacing. The font of the first selected character is used
1765 void LyXText::replaceSelectionWithString(BufferView * bview,
1768 setCursorParUndo(bview);
1771 if (!selection.set()) { // create a dummy selection
1772 selection.end = cursor;
1773 selection.start = cursor;
1776 // Get font setting before we cut
1777 Paragraph::size_type pos = selection.end.pos();
1778 LyXFont const font = selection.start.par()
1779 ->getFontSettings(bview->buffer()->params,
1780 selection.start.pos());
1782 // Insert the new string
1783 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1784 selection.end.par()->insertChar(pos, (*cit), font);
1788 // Cut the selection
1789 cutSelection(bview);
1795 // needed to insert the selection
1796 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1798 Paragraph * par = cursor.par();
1799 Paragraph::size_type pos = cursor.pos();
1800 Paragraph * endpar = cursor.par()->next();
1802 setCursorParUndo(bview);
1804 // only to be sure, should not be neccessary
1805 clearSelection(bview);
1807 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1809 redoParagraphs(bview, cursor, endpar);
1810 setCursor(bview, cursor.par(), cursor.pos());
1811 selection.cursor = cursor;
1812 setCursor(bview, par, pos);
1813 setSelection(bview);
1817 // turns double-CR to single CR, others where converted into one
1818 // blank. Then InsertStringAsLines is called
1819 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1821 string linestr(str);
1822 bool newline_inserted = false;
1823 for (string::size_type i = 0; i < linestr.length(); ++i) {
1824 if (linestr[i] == '\n') {
1825 if (newline_inserted) {
1826 // we know that \r will be ignored by
1827 // InsertStringA. Of course, it is a dirty
1828 // trick, but it works...
1829 linestr[i - 1] = '\r';
1833 newline_inserted = true;
1835 } else if (IsPrintable(linestr[i])) {
1836 newline_inserted = false;
1839 insertStringAsLines(bview, linestr);
1843 bool LyXText::gotoNextInset(BufferView * bview,
1844 std::vector<Inset::Code> const & codes,
1845 string const & contents) const
1847 LyXCursor res = cursor;
1850 if (res.pos() < res.par()->size() - 1) {
1851 res.pos(res.pos() + 1);
1853 res.par(res.par()->next());
1857 } while (res.par() &&
1858 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1859 && (inset = res.par()->getInset(res.pos())) != 0
1860 && find(codes.begin(), codes.end(), inset->lyxCode())
1862 && (contents.empty() ||
1863 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1867 setCursor(bview, res.par(), res.pos());
1874 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1875 Paragraph::size_type pos)
1877 LyXCursor tmpcursor;
1880 Paragraph::size_type z;
1881 Row * row = getRow(par, pos, y);
1883 // is there a break one row above
1884 if (row->previous() && row->previous()->par() == row->par()) {
1885 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1886 if (z >= row->pos()) {
1887 // set the dimensions of the row above
1888 y -= row->previous()->height();
1890 refresh_row = row->previous();
1891 status(bview, LyXText::NEED_MORE_REFRESH);
1893 breakAgain(bview, row->previous());
1895 // set the cursor again. Otherwise
1896 // dangling pointers are possible
1897 setCursor(bview, cursor.par(), cursor.pos(),
1898 false, cursor.boundary());
1899 selection.cursor = cursor;
1904 int const tmpheight = row->height();
1905 Paragraph::size_type const tmplast = rowLast(row);
1909 breakAgain(bview, row);
1910 if (row->height() == tmpheight && rowLast(row) == tmplast)
1911 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1913 status(bview, LyXText::NEED_MORE_REFRESH);
1915 // check the special right address boxes
1916 if (textclasslist.Style(bview->buffer()->params.textclass,
1917 par->getLayout()).margintype
1918 == MARGIN_RIGHT_ADDRESS_BOX) {
1925 redoDrawingOfParagraph(bview, tmpcursor);
1928 // set the cursor again. Otherwise dangling pointers are possible
1929 // also set the selection
1931 if (selection.set()) {
1933 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1934 false, selection.cursor.boundary());
1935 selection.cursor = cursor;
1936 setCursorIntern(bview, selection.start.par(),
1937 selection.start.pos(),
1938 false, selection.start.boundary());
1939 selection.start = cursor;
1940 setCursorIntern(bview, selection.end.par(),
1941 selection.end.pos(),
1942 false, selection.end.boundary());
1943 selection.end = cursor;
1944 setCursorIntern(bview, last_sel_cursor.par(),
1945 last_sel_cursor.pos(),
1946 false, last_sel_cursor.boundary());
1947 last_sel_cursor = cursor;
1950 setCursorIntern(bview, cursor.par(), cursor.pos(),
1951 false, cursor.boundary());
1955 // returns false if inset wasn't found
1956 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1958 // first check the current paragraph
1959 int pos = cursor.par()->getPositionOfInset(inset);
1961 checkParagraph(bview, cursor.par(), pos);
1965 // check every paragraph
1967 Paragraph * par = firstParagraph();
1969 pos = par->getPositionOfInset(inset);
1971 checkParagraph(bview, par, pos);
1981 void LyXText::setCursor(BufferView * bview, Paragraph * par,
1982 Paragraph::size_type pos,
1983 bool setfont, bool boundary) const
1985 LyXCursor old_cursor = cursor;
1986 setCursorIntern(bview, par, pos, setfont, boundary);
1987 deleteEmptyParagraphMechanism(bview, old_cursor);
1991 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
1992 Paragraph::size_type pos, bool boundary) const
1996 cur.boundary(boundary);
1998 // get the cursor y position in text
2000 Row * row = getRow(par, pos, y);
2001 // y is now the beginning of the cursor row
2002 y += row->baseline();
2003 // y is now the cursor baseline
2006 // now get the cursors x position
2008 float fill_separator;
2010 float fill_label_hfill;
2011 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2013 Paragraph::size_type cursor_vpos = 0;
2014 Paragraph::size_type last = rowLastPrintable(row);
2016 if (pos > last + 1) // This shouldn't happen.
2018 else if (pos < row->pos())
2021 if (last < row->pos())
2022 cursor_vpos = row->pos();
2023 else if (pos > last && !boundary)
2024 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2025 ? row->pos() : last + 1;
2026 else if (pos > row->pos() &&
2027 (pos > last || boundary))
2028 /// Place cursor after char at (logical) position pos - 1
2029 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2030 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2032 /// Place cursor before char at (logical) position pos
2033 cursor_vpos = (bidi_level(pos) % 2 == 0)
2034 ? log2vis(pos) : log2vis(pos) + 1;
2036 Paragraph::size_type main_body =
2037 beginningOfMainBody(bview->buffer(), row->par());
2038 if ((main_body > 0) &&
2039 ((main_body-1 > last) ||
2040 !row->par()->isLineSeparator(main_body-1)))
2043 for (Paragraph::size_type vpos = row->pos();
2044 vpos < cursor_vpos; ++vpos) {
2045 pos = vis2log(vpos);
2046 if (main_body > 0 && pos == main_body - 1) {
2047 x += fill_label_hfill +
2048 lyxfont::width(textclasslist.Style(
2049 bview->buffer()->params.textclass,
2050 row->par()->getLayout())
2052 getFont(bview->buffer(), row->par(), -2));
2053 if (row->par()->isLineSeparator(main_body-1))
2054 x -= singleWidth(bview, row->par(),main_body-1);
2056 if (hfillExpansion(bview->buffer(), row, pos)) {
2057 x += singleWidth(bview, row->par(), pos);
2058 if (pos >= main_body)
2061 x += fill_label_hfill;
2062 } else if (row->par()->isSeparator(pos)) {
2063 x += singleWidth(bview, row->par(), pos);
2064 if (pos >= main_body)
2065 x += fill_separator;
2067 x += singleWidth(bview, row->par(), pos);
2076 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2077 Paragraph::size_type pos,
2078 bool setfont, bool boundary) const
2080 InsetText * it = static_cast<InsetText *>(par->inInset());
2081 if (it && (it != inset_owner)) {
2082 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2085 setCursor(bview, cursor, par, pos, boundary);
2087 setCurrentFont(bview);
2092 void LyXText::setCurrentFont(BufferView * bview) const
2094 Paragraph::size_type pos = cursor.pos();
2095 if (cursor.boundary() && pos > 0)
2099 if (pos == cursor.par()->size())
2101 else // potentional bug... BUG (Lgb)
2102 if (cursor.par()->isSeparator(pos)) {
2103 if (pos > cursor.row()->pos() &&
2104 bidi_level(pos) % 2 ==
2105 bidi_level(pos - 1) % 2)
2107 else if (pos + 1 < cursor.par()->size())
2113 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2114 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2116 if (cursor.pos() == cursor.par()->size() &&
2117 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2118 !cursor.boundary()) {
2119 Language const * lang =
2120 cursor.par()->getParLanguage(bview->buffer()->params);
2121 current_font.setLanguage(lang);
2122 current_font.setNumber(LyXFont::OFF);
2123 real_current_font.setLanguage(lang);
2124 real_current_font.setNumber(LyXFont::OFF);
2129 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2131 LyXCursor old_cursor = cursor;
2133 // Get the row first.
2135 Row * row = getRowNearY(y);
2136 cursor.par(row->par());
2139 int column = getColumnNearX(bview, row, x, bound);
2140 cursor.pos(row->pos() + column);
2142 cursor.y(y + row->baseline());
2144 cursor.boundary(bound);
2145 setCurrentFont(bview);
2146 deleteEmptyParagraphMechanism(bview, old_cursor);
2150 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2153 // Get the row first.
2155 Row * row = getRowNearY(y);
2157 int column = getColumnNearX(bview, row, x, bound);
2159 cur.par(row->par());
2160 cur.pos(row->pos() + column);
2162 cur.y(y + row->baseline());
2164 cur.boundary(bound);
2168 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2170 if (cursor.pos() > 0) {
2171 bool boundary = cursor.boundary();
2172 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2173 if (!internal && !boundary &&
2174 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2175 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2176 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2177 Paragraph * par = cursor.par()->previous();
2178 setCursor(bview, par, par->size());
2183 void LyXText::cursorRight(BufferView * bview, bool internal) const
2185 if (!internal && cursor.boundary() &&
2186 !cursor.par()->isNewline(cursor.pos()))
2187 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2188 else if (cursor.pos() < cursor.par()->size()) {
2189 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2191 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2192 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2193 } else if (cursor.par()->next())
2194 setCursor(bview, cursor.par()->next(), 0);
2198 void LyXText::cursorUp(BufferView * bview) const
2200 setCursorFromCoordinates(bview, cursor.x_fix(),
2201 cursor.y() - cursor.row()->baseline() - 1);
2205 void LyXText::cursorDown(BufferView * bview) const
2207 setCursorFromCoordinates(bview, cursor.x_fix(),
2208 cursor.y() - cursor.row()->baseline()
2209 + cursor.row()->height() + 1);
2213 void LyXText::cursorUpParagraph(BufferView * bview) const
2215 if (cursor.pos() > 0) {
2216 setCursor(bview, cursor.par(), 0);
2218 else if (cursor.par()->previous()) {
2219 setCursor(bview, cursor.par()->previous(), 0);
2224 void LyXText::cursorDownParagraph(BufferView * bview) const
2226 if (cursor.par()->next()) {
2227 setCursor(bview, cursor.par()->next(), 0);
2229 setCursor(bview, cursor.par(), cursor.par()->size());
2234 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2235 LyXCursor const & old_cursor) const
2237 // Would be wrong to delete anything if we have a selection.
2238 if (selection.set()) return;
2240 // We allow all kinds of "mumbo-jumbo" when freespacing.
2241 if (textclasslist.Style(bview->buffer()->params.textclass,
2242 old_cursor.par()->getLayout()).free_spacing)
2245 bool deleted = false;
2247 /* Ok I'll put some comments here about what is missing.
2248 I have fixed BackSpace (and thus Delete) to not delete
2249 double-spaces automagically. I have also changed Cut,
2250 Copy and Paste to hopefully do some sensible things.
2251 There are still some small problems that can lead to
2252 double spaces stored in the document file or space at
2253 the beginning of paragraphs. This happens if you have
2254 the cursor betwenn to spaces and then save. Or if you
2255 cut and paste and the selection have a space at the
2256 beginning and then save right after the paste. I am
2257 sure none of these are very hard to fix, but I will
2258 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2259 that I can get some feedback. (Lgb)
2262 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2263 // delete the LineSeparator.
2266 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2267 // delete the LineSeparator.
2270 // If the pos around the old_cursor were spaces, delete one of them.
2271 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2272 // Only if the cursor has really moved
2274 if (old_cursor.pos() > 0
2275 && old_cursor.pos() < old_cursor.par()->size()
2276 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2277 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2278 old_cursor.par()->erase(old_cursor.pos() - 1);
2279 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2281 if (old_cursor.par() == cursor.par() &&
2282 cursor.pos() > old_cursor.pos()) {
2283 setCursorIntern(bview, cursor.par(),
2286 setCursorIntern(bview, cursor.par(),
2292 // Do not delete empty paragraphs with keepempty set.
2293 if ((textclasslist.Style(bview->buffer()->params.textclass,
2294 old_cursor.par()->getLayout())).keepempty)
2297 LyXCursor tmpcursor;
2299 if (old_cursor.par() != cursor.par()) {
2300 if ((old_cursor.par()->size() == 0
2301 || (old_cursor.par()->size() == 1
2302 && old_cursor.par()->isLineSeparator(0)))) {
2303 // ok, we will delete anything
2305 // make sure that you do not delete any environments
2306 status(bview, LyXText::NEED_MORE_REFRESH);
2309 if (old_cursor.row()->previous()) {
2310 refresh_row = old_cursor.row()->previous();
2311 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2313 cursor = old_cursor; // that undo can restore the right cursor position
2314 Paragraph * endpar = old_cursor.par()->next();
2315 if (endpar && endpar->getDepth()) {
2316 while (endpar && endpar->getDepth()) {
2317 endpar = endpar->next();
2320 setUndo(bview, Undo::DELETE,
2326 removeRow(old_cursor.row());
2327 if (ownerParagraph() == old_cursor.par()) {
2328 ownerParagraph(ownerParagraph()->next());
2331 delete old_cursor.par();
2333 /* Breakagain the next par. Needed
2334 * because of the parindent that
2335 * can occur or dissappear. The
2336 * next row can change its height,
2337 * if there is another layout before */
2338 if (refresh_row->next()) {
2339 breakAgain(bview, refresh_row->next());
2340 updateCounters(bview, refresh_row);
2342 setHeightOfRow(bview, refresh_row);
2344 refresh_row = old_cursor.row()->next();
2345 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2348 cursor = old_cursor; // that undo can restore the right cursor position
2349 Paragraph * endpar = old_cursor.par()->next();
2350 if (endpar && endpar->getDepth()) {
2351 while (endpar && endpar->getDepth()) {
2352 endpar = endpar->next();
2355 setUndo(bview, Undo::DELETE,
2361 removeRow(old_cursor.row());
2363 if (ownerParagraph() == old_cursor.par()) {
2364 ownerParagraph(ownerParagraph()->next());
2367 delete old_cursor.par();
2369 /* Breakagain the next par. Needed
2370 because of the parindent that can
2371 occur or dissappear.
2372 The next row can change its height,
2373 if there is another layout before
2376 breakAgain(bview, refresh_row);
2377 updateCounters(bview, refresh_row->previous());
2383 setCursorIntern(bview, cursor.par(), cursor.pos());
2385 if (selection.cursor.par() == old_cursor.par()
2386 && selection.cursor.pos() == selection.cursor.pos()) {
2387 // correct selection
2388 selection.cursor = cursor;
2392 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2393 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2395 setCursorIntern(bview, cursor.par(), cursor.pos());
2396 selection.cursor = cursor;
2403 void LyXText::toggleAppendix(BufferView * bview)
2405 Paragraph * par = cursor.par();
2406 bool start = !par->params().startOfAppendix();
2408 // ensure that we have only one start_of_appendix in this document
2409 Paragraph * tmp = firstParagraph();
2410 for (; tmp; tmp = tmp->next()) {
2411 tmp->params().startOfAppendix(false);
2414 par->params().startOfAppendix(start);
2416 // we can set the refreshing parameters now
2417 status(bview, LyXText::NEED_MORE_REFRESH);
2419 refresh_row = 0; // not needed for full update
2420 updateCounters(bview, 0);
2421 setCursor(bview, cursor.par(), cursor.pos());
2425 Paragraph * LyXText::ownerParagraph() const
2428 return inset_owner->paragraph();
2430 return bv_owner->buffer()->paragraph;
2434 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2437 inset_owner->paragraph(p);
2439 bv_owner->buffer()->paragraph = p;
2444 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2446 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2447 if (op && op->inInset()) {
2448 static_cast<InsetText *>(op->inInset())->paragraph(p);
2451 inset_owner->paragraph(p);
2453 bv_owner->buffer()->paragraph = p;
2460 LyXText::text_status LyXText::status() const
2466 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2469 if ((status_ != NEED_MORE_REFRESH)
2470 || (status_ == NEED_MORE_REFRESH)
2471 && (st != NEED_VERY_LITTLE_REFRESH)) {
2473 if (inset_owner && st != UNCHANGED) {
2474 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2478 #warning Please tell what the intention is here. (Lgb)
2479 // The above does not make any sense, I changed it to what is here,
2480 // but it still does not make much sense. (Lgb)
2481 #warning Sure have a look now! (Jug)
2482 // well as much as I know && binds more then || so the above and the
2483 // below are identical (this for your known use of parentesis!)
2484 // Now some explanation:
2485 // We should only go up with refreshing code so this means that if
2486 // we have a MORE refresh we should never set it to LITTLE if we still
2487 // didn't handle it (and then it will be UNCHANGED. Now as long as
2488 // we stay inside one LyXText this may work but we need to tell the
2489 // outermost LyXText that it should REALLY draw us if there is some
2490 // change in a Inset::LyXText. So you see that when we are inside a
2491 // inset's LyXText we give the LITTLE to the outermost LyXText to
2492 // tell'em that it should redraw the actual row (where the inset
2493 // resides! Capito?!
2495 if ((status_ != NEED_MORE_REFRESH)
2496 || (status_ == NEED_MORE_REFRESH
2497 && st != NEED_VERY_LITTLE_REFRESH))
2500 if (inset_owner && st != UNCHANGED) {
2501 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);