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::IS_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());
427 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
428 if (pos < beginningOfMainBody(buf, par))
429 layoutfont = layout.labelfont;
431 layoutfont = layout.font;
433 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
434 tmpfont.reduce(layoutfont);
435 par->setFont(pos, tmpfont);
440 Paragraph * LyXText::setLayout(BufferView * bview,
441 LyXCursor & cur, LyXCursor & sstart_cur,
442 LyXCursor & send_cur,
443 LyXTextClass::size_type layout)
445 Paragraph * endpar = send_cur.par()->next();
446 Paragraph * undoendpar = endpar;
448 if (endpar && endpar->getDepth()) {
449 while (endpar && endpar->getDepth()) {
450 endpar = endpar->next();
454 endpar = endpar->next(); // because of parindents etc.
457 setUndo(bview, Undo::EDIT,
458 sstart_cur.par(), undoendpar);
460 // ok we have a selection. This is always between sstart_cur
461 // and sel_end cursor
464 LyXLayout const & lyxlayout =
465 textclasslist.Style(bview->buffer()->params.textclass, layout);
467 while (cur.par() != send_cur.par()) {
468 cur.par()->setLayout(layout);
469 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
470 Paragraph * fppar = cur.par();
471 fppar->params().spaceTop(lyxlayout.fill_top ?
472 VSpace(VSpace::VFILL)
473 : VSpace(VSpace::NONE));
474 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
475 VSpace(VSpace::VFILL)
476 : VSpace(VSpace::NONE));
477 if (lyxlayout.margintype == MARGIN_MANUAL)
478 cur.par()->setLabelWidthString(lyxlayout.labelstring());
479 if (lyxlayout.labeltype != LABEL_BIBLIO
481 delete fppar->bibkey;
484 cur.par(cur.par()->next());
486 cur.par()->setLayout(layout);
487 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
488 Paragraph * fppar = cur.par();
489 fppar->params().spaceTop(lyxlayout.fill_top ?
490 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
491 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
492 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
493 if (lyxlayout.margintype == MARGIN_MANUAL)
494 cur.par()->setLabelWidthString(lyxlayout.labelstring());
495 if (lyxlayout.labeltype != LABEL_BIBLIO
497 delete fppar->bibkey;
504 // set layout over selection and make a total rebreak of those paragraphs
505 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
507 LyXCursor tmpcursor = cursor; /* store the current cursor */
509 // if there is no selection just set the layout
510 // of the current paragraph */
511 if (!selection.set()) {
512 selection.start = cursor; // dummy selection
513 selection.end = cursor;
515 Paragraph * endpar = setLayout(bview, cursor, selection.start,
516 selection.end, layout);
517 redoParagraphs(bview, selection.start, endpar);
519 // we have to reset the selection, because the
520 // geometry could have changed
521 setCursor(bview, selection.start.par(),
522 selection.start.pos(), false);
523 selection.cursor = cursor;
524 setCursor(bview, selection.end.par(), selection.end.pos(), false);
525 updateCounters(bview, cursor.row());
528 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
532 // increment depth over selection and
533 // make a total rebreak of those paragraphs
534 void LyXText::incDepth(BufferView * bview)
536 // If there is no selection, just use the current paragraph
537 if (!selection.set()) {
538 selection.start = cursor; // dummy selection
539 selection.end = cursor;
542 // We end at the next paragraph with depth 0
543 Paragraph * endpar = selection.end.par()->next();
545 Paragraph * undoendpar = endpar;
547 if (endpar && endpar->getDepth()) {
548 while (endpar && endpar->getDepth()) {
549 endpar = endpar->next();
553 endpar = endpar->next(); // because of parindents etc.
556 setUndo(bview, Undo::EDIT,
557 selection.start.par(), undoendpar);
559 LyXCursor tmpcursor = cursor; // store the current cursor
561 // ok we have a selection. This is always between sel_start_cursor
562 // and sel_end cursor
563 cursor = selection.start;
565 bool anything_changed = false;
568 // NOTE: you can't change the depth of a bibliography entry
570 textclasslist.Style(bview->buffer()->params.textclass,
571 cursor.par()->getLayout()
572 ).labeltype != LABEL_BIBLIO) {
573 Paragraph * prev = cursor.par()->previous();
576 && (prev->getDepth() - cursor.par()->getDepth() > 0
577 || (prev->getDepth() == cursor.par()->getDepth()
578 && textclasslist.Style(bview->buffer()->params.textclass,
579 prev->getLayout()).isEnvironment()))) {
580 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
581 anything_changed = true;
584 if (cursor.par() == selection.end.par())
586 cursor.par(cursor.par()->next());
589 // if nothing changed set all depth to 0
590 if (!anything_changed) {
591 cursor = selection.start;
592 while (cursor.par() != selection.end.par()) {
593 cursor.par()->params().depth(0);
594 cursor.par(cursor.par()->next());
596 cursor.par()->params().depth(0);
599 redoParagraphs(bview, selection.start, endpar);
601 // we have to reset the selection, because the
602 // geometry could have changed
603 setCursor(bview, selection.start.par(), selection.start.pos());
604 selection.cursor = cursor;
605 setCursor(bview, selection.end.par(), selection.end.pos());
606 updateCounters(bview, cursor.row());
609 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
613 // decrement depth over selection and
614 // make a total rebreak of those paragraphs
615 void LyXText::decDepth(BufferView * bview)
617 // if there is no selection just set the layout
618 // of the current paragraph
619 if (!selection.set()) {
620 selection.start = cursor; // dummy selection
621 selection.end = cursor;
623 Paragraph * endpar = selection.end.par()->next();
624 Paragraph * undoendpar = endpar;
626 if (endpar && endpar->getDepth()) {
627 while (endpar && endpar->getDepth()) {
628 endpar = endpar->next();
632 endpar = endpar->next(); // because of parindents etc.
635 setUndo(bview, Undo::EDIT,
636 selection.start.par(), undoendpar);
638 LyXCursor tmpcursor = cursor; // store the current cursor
640 // ok we have a selection. This is always between sel_start_cursor
641 // and sel_end cursor
642 cursor = selection.start;
645 if (cursor.par()->params().depth()) {
646 cursor.par()->params()
647 .depth(cursor.par()->params().depth() - 1);
649 if (cursor.par() == selection.end.par()) {
652 cursor.par(cursor.par()->next());
655 redoParagraphs(bview, selection.start, endpar);
657 // we have to reset the selection, because the
658 // geometry could have changed
659 setCursor(bview, selection.start.par(),
660 selection.start.pos());
661 selection.cursor = cursor;
662 setCursor(bview, selection.end.par(), selection.end.pos());
663 updateCounters(bview, cursor.row());
666 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
670 // set font over selection and make a total rebreak of those paragraphs
671 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
673 // if there is no selection just set the current_font
674 if (!selection.set()) {
675 // Determine basis font
677 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
679 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
681 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
682 // Update current font
683 real_current_font.update(font, toggleall);
685 // Reduce to implicit settings
686 current_font = real_current_font;
687 current_font.reduce(layoutfont);
688 // And resolve it completely
689 real_current_font.realize(layoutfont,
690 bview->buffer()->params.language);
694 LyXCursor tmpcursor = cursor; // store the current cursor
696 // ok we have a selection. This is always between sel_start_cursor
697 // and sel_end cursor
699 setUndo(bview, Undo::EDIT,
700 selection.start.par(),
701 selection.end.par()->next());
703 cursor = selection.start;
704 while (cursor.par() != selection.end.par() ||
705 (cursor.pos() < selection.end.pos())) {
706 if (cursor.pos() < cursor.par()->size()) {
707 // an open footnote should behave
709 setCharFont(bview, cursor.par(), cursor.pos(),
711 cursor.pos(cursor.pos() + 1);
714 cursor.par(cursor.par()->next());
719 redoParagraphs(bview, selection.start, selection.end.par()->next());
721 // we have to reset the selection, because the
722 // geometry could have changed
723 setCursor(bview, selection.start.par(), selection.start.pos());
724 selection.cursor = cursor;
725 setCursor(bview, selection.end.par(), selection.end.pos());
728 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
729 tmpcursor.boundary());
733 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
735 Row * tmprow = cur.row();
736 int y = cur.y() - tmprow->baseline();
738 setHeightOfRow(bview, tmprow);
740 while (tmprow->previous()
741 && tmprow->previous()->par() == tmprow->par()) {
742 tmprow = tmprow->previous();
743 y -= tmprow->height();
744 setHeightOfRow(bview, tmprow);
747 // we can set the refreshing parameters now
748 status(bview, LyXText::NEED_MORE_REFRESH);
750 refresh_row = tmprow;
751 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
755 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
757 Row * tmprow = cur.row();
759 int y = cur.y() - tmprow->baseline();
760 setHeightOfRow(bview, tmprow);
762 while (tmprow->previous()
763 && tmprow->previous()->par() == tmprow->par()) {
764 tmprow = tmprow->previous();
765 y -= tmprow->height();
768 // we can set the refreshing parameters now
769 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
771 refresh_row = tmprow;
773 status(bview, LyXText::NEED_MORE_REFRESH);
774 setCursor(bview, cur.par(), cur.pos());
778 // deletes and inserts again all paragaphs between the cursor
779 // and the specified par
780 // This function is needed after SetLayout and SetFont etc.
781 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
782 Paragraph const * endpar) const
785 Paragraph * tmppar = 0;
786 Paragraph * first_phys_par = 0;
788 Row * tmprow = cur.row();
790 int y = cur.y() - tmprow->baseline();
792 if (!tmprow->previous()) {
793 // a trick/hack for UNDO
794 // Can somebody please tell me _why_ this solves
796 first_phys_par = firstParagraph();
798 first_phys_par = tmprow->par();
799 while (tmprow->previous()
800 && 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() 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) {
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) {
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);
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)
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());
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
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;
2167 // Get the row first.
2169 Row * row = getRowNearY(y);
2172 int column = getColumnNearX(bview, row, x, bound);
2174 cursor.par(row->par());
2175 cursor.pos(row->pos() + column);
2177 cursor.y(y + row->baseline());
2179 cursor.boundary(bound);
2181 setCursorFromCoordinates(bview, cursor, x, y);
2183 setCurrentFont(bview);
2184 deleteEmptyParagraphMechanism(bview, old_cursor);
2188 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2191 // Get the row first.
2193 Row * row = getRowNearY(y);
2195 int const column = getColumnNearX(bview, row, x, bound);
2197 cur.par(row->par());
2198 cur.pos(row->pos() + column);
2200 cur.y(y + row->baseline());
2202 cur.boundary(bound);
2206 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2208 if (cursor.pos() > 0) {
2209 bool boundary = cursor.boundary();
2210 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2211 if (!internal && !boundary &&
2212 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2213 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2214 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2215 Paragraph * par = cursor.par()->previous();
2216 setCursor(bview, par, par->size());
2221 void LyXText::cursorRight(BufferView * bview, bool internal) const
2223 if (!internal && cursor.boundary() &&
2224 !cursor.par()->isNewline(cursor.pos()))
2225 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2226 else if (cursor.pos() < cursor.par()->size()) {
2227 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2229 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2230 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2231 } else if (cursor.par()->next())
2232 setCursor(bview, cursor.par()->next(), 0);
2236 void LyXText::cursorUp(BufferView * bview) const
2238 setCursorFromCoordinates(bview, cursor.x_fix(),
2239 cursor.y() - cursor.row()->baseline() - 1);
2243 void LyXText::cursorDown(BufferView * bview) const
2245 setCursorFromCoordinates(bview, cursor.x_fix(),
2246 cursor.y() - cursor.row()->baseline()
2247 + cursor.row()->height() + 1);
2251 void LyXText::cursorUpParagraph(BufferView * bview) const
2253 if (cursor.pos() > 0) {
2254 setCursor(bview, cursor.par(), 0);
2256 else if (cursor.par()->previous()) {
2257 setCursor(bview, cursor.par()->previous(), 0);
2262 void LyXText::cursorDownParagraph(BufferView * bview) const
2264 if (cursor.par()->next()) {
2265 setCursor(bview, cursor.par()->next(), 0);
2267 setCursor(bview, cursor.par(), cursor.par()->size());
2272 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2273 LyXCursor const & old_cursor) const
2275 // Would be wrong to delete anything if we have a selection.
2276 if (selection.set()) return;
2278 // We allow all kinds of "mumbo-jumbo" when freespacing.
2279 if (textclasslist.Style(bview->buffer()->params.textclass,
2280 old_cursor.par()->getLayout()).free_spacing)
2283 bool deleted = false;
2285 /* Ok I'll put some comments here about what is missing.
2286 I have fixed BackSpace (and thus Delete) to not delete
2287 double-spaces automagically. I have also changed Cut,
2288 Copy and Paste to hopefully do some sensible things.
2289 There are still some small problems that can lead to
2290 double spaces stored in the document file or space at
2291 the beginning of paragraphs. This happens if you have
2292 the cursor betwenn to spaces and then save. Or if you
2293 cut and paste and the selection have a space at the
2294 beginning and then save right after the paste. I am
2295 sure none of these are very hard to fix, but I will
2296 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2297 that I can get some feedback. (Lgb)
2300 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2301 // delete the LineSeparator.
2304 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2305 // delete the LineSeparator.
2308 // If the pos around the old_cursor were spaces, delete one of them.
2309 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2310 // Only if the cursor has really moved
2312 if (old_cursor.pos() > 0
2313 && old_cursor.pos() < old_cursor.par()->size()
2314 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2315 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2316 old_cursor.par()->erase(old_cursor.pos() - 1);
2317 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2319 if (old_cursor.par() == cursor.par() &&
2320 cursor.pos() > old_cursor.pos()) {
2321 setCursorIntern(bview, cursor.par(),
2324 setCursorIntern(bview, cursor.par(),
2330 // Do not delete empty paragraphs with keepempty set.
2331 if ((textclasslist.Style(bview->buffer()->params.textclass,
2332 old_cursor.par()->getLayout())).keepempty)
2335 LyXCursor tmpcursor;
2337 if (old_cursor.par() != cursor.par()) {
2338 if ((old_cursor.par()->size() == 0
2339 || (old_cursor.par()->size() == 1
2340 && old_cursor.par()->isLineSeparator(0)))) {
2341 // ok, we will delete anything
2343 // make sure that you do not delete any environments
2344 status(bview, LyXText::NEED_MORE_REFRESH);
2347 if (old_cursor.row()->previous()) {
2348 refresh_row = old_cursor.row()->previous();
2349 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2351 cursor = old_cursor; // that undo can restore the right cursor position
2352 Paragraph * endpar = old_cursor.par()->next();
2353 if (endpar && endpar->getDepth()) {
2354 while (endpar && endpar->getDepth()) {
2355 endpar = endpar->next();
2358 setUndo(bview, Undo::DELETE,
2364 removeRow(old_cursor.row());
2365 if (ownerParagraph() == old_cursor.par()) {
2366 ownerParagraph(ownerParagraph()->next());
2369 delete old_cursor.par();
2371 /* Breakagain the next par. Needed
2372 * because of the parindent that
2373 * can occur or dissappear. The
2374 * next row can change its height,
2375 * if there is another layout before */
2376 if (refresh_row->next()) {
2377 breakAgain(bview, refresh_row->next());
2378 updateCounters(bview, refresh_row);
2380 setHeightOfRow(bview, refresh_row);
2382 refresh_row = old_cursor.row()->next();
2383 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2386 cursor = old_cursor; // that undo can restore the right cursor position
2387 Paragraph * endpar = old_cursor.par()->next();
2388 if (endpar && endpar->getDepth()) {
2389 while (endpar && endpar->getDepth()) {
2390 endpar = endpar->next();
2393 setUndo(bview, Undo::DELETE,
2399 removeRow(old_cursor.row());
2401 if (ownerParagraph() == old_cursor.par()) {
2402 ownerParagraph(ownerParagraph()->next());
2405 delete old_cursor.par();
2407 /* Breakagain the next par. Needed
2408 because of the parindent that can
2409 occur or dissappear.
2410 The next row can change its height,
2411 if there is another layout before
2414 breakAgain(bview, refresh_row);
2415 updateCounters(bview, refresh_row->previous());
2421 setCursorIntern(bview, cursor.par(), cursor.pos());
2423 if (selection.cursor.par() == old_cursor.par()
2424 && selection.cursor.pos() == selection.cursor.pos()) {
2425 // correct selection
2426 selection.cursor = cursor;
2430 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2431 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2433 setCursorIntern(bview, cursor.par(), cursor.pos());
2434 selection.cursor = cursor;
2441 void LyXText::toggleAppendix(BufferView * bview)
2443 Paragraph * par = cursor.par();
2444 bool start = !par->params().startOfAppendix();
2446 // ensure that we have only one start_of_appendix in this document
2447 Paragraph * tmp = firstParagraph();
2448 for (; tmp; tmp = tmp->next()) {
2449 tmp->params().startOfAppendix(false);
2452 par->params().startOfAppendix(start);
2454 // we can set the refreshing parameters now
2455 status(bview, LyXText::NEED_MORE_REFRESH);
2457 refresh_row = 0; // not needed for full update
2458 updateCounters(bview, 0);
2459 setCursor(bview, cursor.par(), cursor.pos());
2463 Paragraph * LyXText::ownerParagraph() const
2466 return inset_owner->paragraph();
2468 return bv_owner->buffer()->paragraph;
2472 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2475 inset_owner->paragraph(p);
2477 bv_owner->buffer()->paragraph = p;
2482 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2484 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2485 if (op && op->inInset()) {
2486 static_cast<InsetText *>(op->inInset())->paragraph(p);
2489 inset_owner->paragraph(p);
2491 bv_owner->buffer()->paragraph = p;
2498 LyXText::text_status LyXText::status() const
2504 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2507 if ((status_ != NEED_MORE_REFRESH)
2508 || (status_ == NEED_MORE_REFRESH)
2509 && (st != NEED_VERY_LITTLE_REFRESH)) {
2511 if (inset_owner && st != UNCHANGED) {
2512 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2516 #warning Please tell what the intention is here. (Lgb)
2517 // The above does not make any sense, I changed it to what is here,
2518 // but it still does not make much sense. (Lgb)
2519 #warning Sure have a look now! (Jug)
2520 // well as much as I know && binds more then || so the above and the
2521 // below are identical (this for your known use of parentesis!)
2522 // Now some explanation:
2523 // We should only go up with refreshing code so this means that if
2524 // we have a MORE refresh we should never set it to LITTLE if we still
2525 // didn't handle it (and then it will be UNCHANGED. Now as long as
2526 // we stay inside one LyXText this may work but we need to tell the
2527 // outermost LyXText that it should REALLY draw us if there is some
2528 // change in a Inset::LyXText. So you see that when we are inside a
2529 // inset's LyXText we give the LITTLE to the outermost LyXText to
2530 // tell'em that it should redraw the actual row (where the inset
2531 // resides! Capito?!
2533 if ((status_ != NEED_MORE_REFRESH)
2534 || (status_ == NEED_MORE_REFRESH
2535 && st != NEED_VERY_LITTLE_REFRESH))
2538 if (inset_owner && st != UNCHANGED) {
2539 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2540 if (!bview->text->refresh_row) {
2541 bview->text->refresh_row = bview->text->cursor.row();
2542 bview->text->refresh_y = bview->text->cursor.y() -
2543 bview->text->cursor.row()->baseline();