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 "frontends/LyXView.h"
21 #include "undo_funcs.h"
23 #include "bufferparams.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
32 #include "FloatList.h"
34 #include "ParagraphParameters.h"
36 #include "insets/inseterror.h"
37 #include "insets/insetbib.h"
38 #include "insets/insetspecialchar.h"
39 #include "insets/insettext.h"
40 #include "insets/insetfloat.h"
42 #include "support/LAssert.h"
43 #include "support/textutils.h"
44 #include "support/lstrings.h"
54 LyXText::LyXText(BufferView * bv)
55 : number_of_rows(0), height(0), width(0), first_y(0),
56 bv_owner(bv), inset_owner(0), the_locking_inset(0),
57 need_break_row(0), refresh_y(0), refresh_row(0),
58 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
62 LyXText::LyXText(InsetText * inset)
63 : number_of_rows(0), height(0), width(0), first_y(0),
64 bv_owner(0), inset_owner(inset), the_locking_inset(0),
65 need_break_row(0), refresh_y(0), refresh_row(0),
66 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
70 void LyXText::init(BufferView * bview, bool reinit)
73 // Delete all rows, this does not touch the paragraphs!
74 Row * tmprow = firstrow;
76 tmprow = firstrow->next();
85 copylayouttype.erase();
86 number_of_rows = first_y = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
95 insertParagraph(bview, par, lastrow);
98 setCursorIntern(bview, firstrow->par(), 0);
99 selection.cursor = cursor;
105 // Delete all rows, this does not touch the paragraphs!
106 Row * tmprow = firstrow;
108 tmprow = firstrow->next();
117 LyXFont const realizeFont(LyXFont const & font,
121 LyXTextClass const & tclass = buf->params.getLyXTextClass();
122 LyXFont tmpfont(font);
123 Paragraph::depth_type par_depth = par->getDepth();
125 // Resolve against environment font information
126 while (par && par_depth && !tmpfont.resolved()) {
127 par = par->outerHook();
129 #ifndef INHERIT_LANGUAGE
130 tmpfont.realize(par->layout()->font);
132 tmpfont.realize(tclass[par->layout()]->font,
133 buf->params.language);
135 par_depth = par->getDepth();
139 #ifndef INHERIT_LANGUAGE
140 tmpfont.realize(tclass.defaultfont());
142 tmpfont.realize(tclass.defaultfont(), buf->params.language);
151 // Gets the fully instantiated font at a given position in a paragraph
152 // Basically the same routine as Paragraph::getFont() in paragraph.C.
153 // The difference is that this one is used for displaying, and thus we
154 // are allowed to make cosmetic improvements. For instance make footnotes
156 // If position is -1, we get the layout font of the paragraph.
157 // If position is -2, we get the font of the manual label of the paragraph.
158 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
161 lyx::Assert(pos >= 0);
163 LyXLayout_ptr const & layout = par->layout();
165 Paragraph::depth_type par_depth = par->getDepth();
166 // We specialize the 95% common case:
168 if (layout->labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params, pos);
173 par->inInset()->getDrawFont(f);
174 #ifndef INHERIT_LANGUAGE
175 return f.realize(layout->reslabelfont);
177 return f.realize(layout.reslabelfont, buf->params.language);
180 LyXFont f = par->getFontSettings(buf->params, pos);
182 par->inInset()->getDrawFont(f);
183 #ifndef INHERIT_LANGUAGE
184 return f.realize(layout->resfont);
186 return f.realize(layout.resfont, buf->params.language);
191 // The uncommon case need not be optimized as much
195 if (pos < beginningOfMainBody(buf, par)) {
197 layoutfont = layout->labelfont;
200 layoutfont = layout->font;
203 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
204 #ifndef INHERIT_LANGUAGE
205 tmpfont.realize(layoutfont);
207 tmpfont.realize(layoutfont, buf->params.language);
210 par->inInset()->getDrawFont(tmpfont);
212 return realizeFont(tmpfont, buf, par);
216 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
218 LyXLayout_ptr const & layout = par->layout();
220 Paragraph::depth_type par_depth = par->getDepth();
223 return layout->resfont;
226 return realizeFont(layout->font, buf, par);
230 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
232 LyXLayout_ptr const & layout = par->layout();
234 Paragraph::depth_type par_depth = par->getDepth();
237 return layout->reslabelfont;
240 return realizeFont(layout->labelfont, buf, par);
244 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
245 pos_type pos, LyXFont const & fnt,
248 Buffer const * buf = bv->buffer();
249 LyXFont font = getFont(buf, par, pos);
250 font.update(fnt, buf->params.language, toggleall);
251 // Let the insets convert their font
252 if (par->isInset(pos)) {
253 Inset * inset = par->getInset(pos);
254 if (isEditableInset(inset)) {
255 UpdatableInset * uinset =
256 static_cast<UpdatableInset *>(inset);
257 uinset->setFont(bv, fnt, toggleall, true);
261 // Plug thru to version below:
262 setCharFont(buf, par, pos, font);
266 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
267 pos_type pos, LyXFont const & fnt)
271 LyXTextClass const & tclass = buf->params.getLyXTextClass();
272 LyXLayout_ptr const & layout = par->layout();
274 // Get concrete layout font to reduce against
277 if (pos < beginningOfMainBody(buf, par))
278 layoutfont = layout->labelfont;
280 layoutfont = layout->font;
282 // Realize against environment font information
283 if (par->getDepth()) {
284 Paragraph * tp = par;
285 while (!layoutfont.resolved() && tp && tp->getDepth()) {
286 tp = tp->outerHook();
288 #ifndef INHERIT_LANGUAGE
289 layoutfont.realize(tp->layout()->font);
291 layoutfont.realize(tclass[tp->layout()].font,
292 buf->params.language);
297 #ifndef INHERIT_LANGUAGE
298 layoutfont.realize(tclass.defaultfont());
300 layoutfont.realize(tclass.defaultfont(), buf->params.language);
303 // Now, reduce font against full layout font
304 font.reduce(layoutfont);
306 par->setFont(pos, font);
310 // inserts a new row behind the specified row, increments
311 // the touched counters
312 void LyXText::insertRow(Row * row, Paragraph * par,
315 Row * tmprow = new Row;
318 tmprow->next(firstrow);
321 tmprow->previous(row);
322 tmprow->next(row->next());
327 tmprow->next()->previous(tmprow);
329 if (tmprow->previous())
330 tmprow->previous()->next(tmprow);
342 // removes the row and reset the touched counters
343 void LyXText::removeRow(Row * row) const
345 Row * row_prev = row->previous();
347 row->next()->previous(row_prev);
349 firstrow = row->next();
350 // lyx::Assert(firstrow);
352 row_prev->next(row->next());
354 if (row == lastrow) {
355 lyx::Assert(!row->next());
358 if (refresh_row == row) {
359 refresh_row = row_prev ? row_prev : row->next();
360 // what about refresh_y, refresh_height
363 height -= row->height(); // the text becomes smaller
366 --number_of_rows; // one row less
370 // remove all following rows of the paragraph of the specified row.
371 void LyXText::removeParagraph(Row * row) const
373 Paragraph * tmppar = row->par();
377 while (row && row->par() == tmppar) {
378 tmprow = row->next();
385 // insert the specified paragraph behind the specified row
386 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
389 insertRow(row, par, 0); /* insert a new row, starting
392 setCounter(bview->buffer(), par); // set the counters
394 // and now append the whole paragraph behind the new row
397 appendParagraph(bview, firstrow);
399 row->next()->height(0);
400 appendParagraph(bview, row->next());
405 Inset * LyXText::getInset() const
408 if (cursor.pos() == 0 && cursor.par()->bibkey) {
409 inset = cursor.par()->bibkey;
410 } else if (cursor.pos() < cursor.par()->size()
411 && cursor.par()->isInset(cursor.pos())) {
412 inset = cursor.par()->getInset(cursor.pos());
418 void LyXText::toggleInset(BufferView * bview)
420 Inset * inset = getInset();
421 // is there an editable inset at cursor position?
422 if (!isEditableInset(inset)) {
423 // No, try to see if we are inside a collapsable inset
424 if (inset_owner && inset_owner->owner()
425 && inset_owner->owner()->isOpen()) {
426 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
427 inset_owner->owner()->close(bview);
428 bview->getLyXText()->cursorRight(bview);
432 //bview->owner()->message(inset->editMessage());
434 // do we want to keep this?? (JMarc)
435 if (!isHighlyEditableInset(inset))
436 setCursorParUndo(bview);
438 if (inset->isOpen()) {
444 inset->open(bview, !inset->isOpen());
449 /* used in setlayout */
450 // Asger is not sure we want to do this...
451 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
454 LyXLayout_ptr const & layout = par->layout();
457 for (pos_type pos = 0; pos < par->size(); ++pos) {
458 if (pos < beginningOfMainBody(buf, par))
459 layoutfont = layout->labelfont;
461 layoutfont = layout->font;
463 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
464 tmpfont.reduce(layoutfont);
465 par->setFont(pos, tmpfont);
470 Paragraph * LyXText::setLayout(BufferView * bview,
471 LyXCursor & cur, LyXCursor & sstart_cur,
472 LyXCursor & send_cur,
473 string const & layout)
475 Paragraph * endpar = send_cur.par()->next();
476 Paragraph * undoendpar = endpar;
478 if (endpar && endpar->getDepth()) {
479 while (endpar && endpar->getDepth()) {
480 endpar = endpar->next();
484 endpar = endpar->next(); // because of parindents etc.
487 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
489 // ok we have a selection. This is always between sstart_cur
490 // and sel_end cursor
492 Paragraph * par = sstart_cur.par();
493 Paragraph * epar = send_cur.par()->next();
495 LyXLayout_ptr const & lyxlayout =
496 bview->buffer()->params.getLyXTextClass()[layout];
499 par->applyLayout(lyxlayout);
500 makeFontEntriesLayoutSpecific(bview->buffer(), par);
501 Paragraph * fppar = par;
502 fppar->params().spaceTop(lyxlayout->fill_top ?
503 VSpace(VSpace::VFILL)
504 : VSpace(VSpace::NONE));
505 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
506 VSpace(VSpace::VFILL)
507 : VSpace(VSpace::NONE));
508 if (lyxlayout->margintype == MARGIN_MANUAL)
509 par->setLabelWidthString(lyxlayout->labelstring());
510 if (lyxlayout->labeltype != LABEL_BIBLIO
512 delete fppar->bibkey;
517 } while (par != epar);
523 // set layout over selection and make a total rebreak of those paragraphs
524 void LyXText::setLayout(BufferView * bview, string const & layout)
526 LyXCursor tmpcursor = cursor; /* store the current cursor */
528 // if there is no selection just set the layout
529 // of the current paragraph */
530 if (!selection.set()) {
531 selection.start = cursor; // dummy selection
532 selection.end = cursor;
534 Paragraph * endpar = setLayout(bview, cursor, selection.start,
535 selection.end, layout);
536 redoParagraphs(bview, selection.start, endpar);
538 // we have to reset the selection, because the
539 // geometry could have changed
540 setCursor(bview, selection.start.par(),
541 selection.start.pos(), false);
542 selection.cursor = cursor;
543 setCursor(bview, selection.end.par(), selection.end.pos(), false);
544 updateCounters(bview, cursor.row());
547 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
551 // increment depth over selection and
552 // make a total rebreak of those paragraphs
553 void LyXText::incDepth(BufferView * bview)
555 // If there is no selection, just use the current paragraph
556 if (!selection.set()) {
557 selection.start = cursor; // dummy selection
558 selection.end = cursor;
561 // We end at the next paragraph with depth 0
562 Paragraph * endpar = selection.end.par()->next();
564 Paragraph * undoendpar = endpar;
566 if (endpar && endpar->getDepth()) {
567 while (endpar && endpar->getDepth()) {
568 endpar = endpar->next();
572 endpar = endpar->next(); // because of parindents etc.
575 setUndo(bview, Undo::EDIT,
576 selection.start.par(), undoendpar);
578 LyXCursor tmpcursor = cursor; // store the current cursor
580 // ok we have a selection. This is always between sel_start_cursor
581 // and sel_end cursor
582 cursor = selection.start;
584 bool anything_changed = false;
587 // NOTE: you can't change the depth of a bibliography entry
588 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
589 Paragraph * prev = cursor.par()->previous();
592 if (cursor.par()->getDepth()
593 < prev->getMaxDepthAfter()) {
594 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
595 anything_changed = true;
599 if (cursor.par() == selection.end.par())
601 cursor.par(cursor.par()->next());
604 // if nothing changed set all depth to 0
605 if (!anything_changed) {
606 cursor = selection.start;
607 while (cursor.par() != selection.end.par()) {
608 cursor.par()->params().depth(0);
609 cursor.par(cursor.par()->next());
611 cursor.par()->params().depth(0);
614 redoParagraphs(bview, selection.start, endpar);
616 // we have to reset the selection, because the
617 // geometry could have changed
618 setCursor(bview, selection.start.par(), selection.start.pos());
619 selection.cursor = cursor;
620 setCursor(bview, selection.end.par(), selection.end.pos());
621 updateCounters(bview, cursor.row());
624 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
628 // decrement depth over selection and
629 // make a total rebreak of those paragraphs
630 void LyXText::decDepth(BufferView * bview)
632 // if there is no selection just set the layout
633 // of the current paragraph
634 if (!selection.set()) {
635 selection.start = cursor; // dummy selection
636 selection.end = cursor;
638 Paragraph * endpar = selection.end.par()->next();
639 Paragraph * undoendpar = endpar;
641 if (endpar && endpar->getDepth()) {
642 while (endpar && endpar->getDepth()) {
643 endpar = endpar->next();
647 endpar = endpar->next(); // because of parindents etc.
650 setUndo(bview, Undo::EDIT,
651 selection.start.par(), undoendpar);
653 LyXCursor tmpcursor = cursor; // store the current cursor
655 // ok we have a selection. This is always between sel_start_cursor
656 // and sel_end cursor
657 cursor = selection.start;
660 if (cursor.par()->params().depth()) {
661 cursor.par()->params()
662 .depth(cursor.par()->params().depth() - 1);
664 if (cursor.par() == selection.end.par()) {
667 cursor.par(cursor.par()->next());
670 redoParagraphs(bview, selection.start, endpar);
672 // we have to reset the selection, because the
673 // geometry could have changed
674 setCursor(bview, selection.start.par(),
675 selection.start.pos());
676 selection.cursor = cursor;
677 setCursor(bview, selection.end.par(), selection.end.pos());
678 updateCounters(bview, cursor.row());
681 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
685 // set font over selection and make a total rebreak of those paragraphs
686 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
688 // if there is no selection just set the current_font
689 if (!selection.set()) {
690 // Determine basis font
692 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
694 layoutfont = getLabelFont(bview->buffer(),
697 layoutfont = getLayoutFont(bview->buffer(),
700 // Update current font
701 real_current_font.update(font,
702 bview->buffer()->params.language,
705 // Reduce to implicit settings
706 current_font = real_current_font;
707 current_font.reduce(layoutfont);
708 // And resolve it completely
709 #ifndef INHERIT_LANGUAGE
710 real_current_font.realize(layoutfont);
712 real_current_font.realize(layoutfont,
713 bview->buffer()->params.language);
718 LyXCursor tmpcursor = cursor; // store the current cursor
720 // ok we have a selection. This is always between sel_start_cursor
721 // and sel_end cursor
723 setUndo(bview, Undo::EDIT,
724 selection.start.par(), selection.end.par()->next());
726 cursor = selection.start;
727 while (cursor.par() != selection.end.par() ||
728 cursor.pos() < selection.end.pos())
730 if (cursor.pos() < cursor.par()->size()) {
731 // an open footnote should behave like a closed one
732 setCharFont(bview, cursor.par(), cursor.pos(),
734 cursor.pos(cursor.pos() + 1);
737 cursor.par(cursor.par()->next());
742 redoParagraphs(bview, selection.start, selection.end.par()->next());
744 // we have to reset the selection, because the
745 // geometry could have changed, but we keep
746 // it for user convenience
747 setCursor(bview, selection.start.par(), selection.start.pos());
748 selection.cursor = cursor;
749 setCursor(bview, selection.end.par(), selection.end.pos());
751 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
752 tmpcursor.boundary());
756 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
758 Row * tmprow = cur.row();
759 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();
767 setHeightOfRow(bview, tmprow);
770 // we can set the refreshing parameters now
771 status(bview, LyXText::NEED_MORE_REFRESH);
773 refresh_row = tmprow;
774 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
778 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
780 Row * tmprow = cur.row();
782 int y = cur.y() - tmprow->baseline();
783 setHeightOfRow(bview, tmprow);
785 while (tmprow->previous()
786 && tmprow->previous()->par() == tmprow->par()) {
787 tmprow = tmprow->previous();
788 y -= tmprow->height();
791 // we can set the refreshing parameters now
792 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
794 refresh_row = tmprow;
796 status(bview, LyXText::NEED_MORE_REFRESH);
797 setCursor(bview, cur.par(), cur.pos());
801 // deletes and inserts again all paragaphs between the cursor
802 // and the specified par
803 // This function is needed after SetLayout and SetFont etc.
804 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
805 Paragraph const * endpar) const
808 Paragraph * tmppar = 0;
809 Paragraph * first_phys_par = 0;
811 Row * tmprow = cur.row();
813 int y = cur.y() - tmprow->baseline();
815 if (!tmprow->previous()) {
816 // a trick/hack for UNDO
817 // This is needed because in an UNDO/REDO we could have changed
818 // the ownerParagrah() so the paragraph inside the row is NOT
819 // my really first par anymore. Got it Lars ;) (Jug 20011206)
820 first_phys_par = ownerParagraph();
822 first_phys_par = tmprow->par();
823 while (tmprow->previous()
824 && tmprow->previous()->par() == first_phys_par)
826 tmprow = tmprow->previous();
827 y -= tmprow->height();
831 // we can set the refreshing parameters now
832 status(bview, LyXText::NEED_MORE_REFRESH);
834 refresh_row = tmprow->previous(); /* the real refresh row will
835 be deleted, so I store
839 tmppar = tmprow->next()->par();
842 while (tmprow->next() && tmppar != endpar) {
843 removeRow(tmprow->next());
844 if (tmprow->next()) {
845 tmppar = tmprow->next()->par();
851 // remove the first one
852 tmprow2 = tmprow; /* this is because tmprow->previous()
854 tmprow = tmprow->previous();
857 tmppar = first_phys_par;
861 insertParagraph(bview, tmppar, tmprow);
865 while (tmprow->next()
866 && tmprow->next()->par() == tmppar) {
867 tmprow = tmprow->next();
869 tmppar = tmppar->next();
871 } while (tmppar && tmppar != endpar);
873 // this is because of layout changes
875 refresh_y -= refresh_row->height();
876 setHeightOfRow(bview, refresh_row);
878 refresh_row = firstrow;
880 setHeightOfRow(bview, refresh_row);
883 if (tmprow && tmprow->next())
884 setHeightOfRow(bview, tmprow->next());
888 void LyXText::fullRebreak(BufferView * bview)
894 if (need_break_row) {
895 breakAgain(bview, need_break_row);
902 // important for the screen
905 // the cursor set functions have a special mechanism. When they
906 // realize, that you left an empty paragraph, they will delete it.
907 // They also delete the corresponding row
909 // need the selection cursor:
910 void LyXText::setSelection(BufferView * bview)
912 bool const lsel = selection.set();
914 if (!selection.set()) {
915 last_sel_cursor = selection.cursor;
916 selection.start = selection.cursor;
917 selection.end = selection.cursor;
922 // first the toggling area
923 if (cursor.y() < last_sel_cursor.y()
924 || (cursor.y() == last_sel_cursor.y()
925 && cursor.x() < last_sel_cursor.x())) {
926 toggle_end_cursor = last_sel_cursor;
927 toggle_cursor = cursor;
929 toggle_end_cursor = cursor;
930 toggle_cursor = last_sel_cursor;
933 last_sel_cursor = cursor;
935 // and now the whole selection
937 if (selection.cursor.par() == cursor.par())
938 if (selection.cursor.pos() < cursor.pos()) {
939 selection.end = cursor;
940 selection.start = selection.cursor;
942 selection.end = selection.cursor;
943 selection.start = cursor;
945 else if (selection.cursor.y() < cursor.y() ||
946 (selection.cursor.y() == cursor.y()
947 && selection.cursor.x() < cursor.x())) {
948 selection.end = cursor;
949 selection.start = selection.cursor;
952 selection.end = selection.cursor;
953 selection.start = cursor;
956 // a selection with no contents is not a selection
957 if (selection.start.par() == selection.end.par() &&
958 selection.start.pos() == selection.end.pos())
959 selection.set(false);
961 if (inset_owner && (selection.set() || lsel))
962 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
966 string const LyXText::selectionAsString(Buffer const * buffer,
969 if (!selection.set()) return string();
971 // should be const ...
972 Paragraph * startpar(selection.start.par());
973 Paragraph * endpar(selection.end.par());
974 pos_type const startpos(selection.start.pos());
975 pos_type const endpos(selection.end.pos());
977 if (startpar == endpar) {
978 return startpar->asString(buffer, startpos, endpos, label);
983 // First paragraph in selection
984 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
986 // The paragraphs in between (if any)
987 LyXCursor tmpcur(selection.start);
988 tmpcur.par(tmpcur.par()->next());
989 while (tmpcur.par() != endpar) {
990 result += tmpcur.par()->asString(buffer, 0,
991 tmpcur.par()->size(),
993 tmpcur.par(tmpcur.par()->next());
996 // Last paragraph in selection
997 result += endpar->asString(buffer, 0, endpos, label);
1003 void LyXText::clearSelection() const
1005 selection.set(false);
1006 selection.mark(false);
1007 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1008 // reset this in the bv_owner!
1009 if (bv_owner && bv_owner->text)
1010 bv_owner->text->xsel_cache.set(false);
1014 void LyXText::cursorHome(BufferView * bview) const
1016 setCursor(bview, cursor.par(), cursor.row()->pos());
1020 void LyXText::cursorEnd(BufferView * bview) const
1022 if (!cursor.row()->next()
1023 || cursor.row()->next()->par() != cursor.row()->par()) {
1024 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1026 if (cursor.par()->size() &&
1027 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1028 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1029 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1031 setCursor(bview,cursor.par(),
1032 rowLast(cursor.row()) + 1);
1038 void LyXText::cursorTop(BufferView * bview) const
1040 while (cursor.par()->previous())
1041 cursor.par(cursor.par()->previous());
1042 setCursor(bview, cursor.par(), 0);
1046 void LyXText::cursorBottom(BufferView * bview) const
1048 while (cursor.par()->next())
1049 cursor.par(cursor.par()->next());
1050 setCursor(bview, cursor.par(), cursor.par()->size());
1054 void LyXText::toggleFree(BufferView * bview,
1055 LyXFont const & font, bool toggleall)
1057 // If the mask is completely neutral, tell user
1058 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1059 // Could only happen with user style
1060 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1064 // Try implicit word selection
1065 // If there is a change in the language the implicit word selection
1067 LyXCursor resetCursor = cursor;
1068 bool implicitSelection = (font.language() == ignore_language
1069 && font.number() == LyXFont::IGNORE)
1070 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1073 setFont(bview, font, toggleall);
1075 // Implicit selections are cleared afterwards
1076 //and cursor is set to the original position.
1077 if (implicitSelection) {
1079 cursor = resetCursor;
1080 setCursor(bview, cursor.par(), cursor.pos());
1081 selection.cursor = cursor;
1084 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1088 string LyXText::getStringToIndex(BufferView * bview)
1092 // Try implicit word selection
1093 // If there is a change in the language the implicit word selection
1095 LyXCursor const reset_cursor = cursor;
1096 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1098 if (!selection.set()) {
1099 bview->owner()->message(_("Nothing to index!"));
1102 if (selection.start.par() != selection.end.par()) {
1103 bview->owner()->message(_("Cannot index more than one paragraph!"));
1107 idxstring = selectionAsString(bview->buffer(), false);
1109 // Implicit selections are cleared afterwards
1110 //and cursor is set to the original position.
1111 if (implicitSelection) {
1113 cursor = reset_cursor;
1114 setCursor(bview, cursor.par(), cursor.pos());
1115 selection.cursor = cursor;
1121 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1122 Paragraph const * par) const
1124 if (par->layout()->labeltype != LABEL_MANUAL)
1127 return par->beginningOfMainBody();
1131 // the DTP switches for paragraphs. LyX will store them in the first
1132 // physicla paragraph. When a paragraph is broken, the top settings rest,
1133 // the bottom settings are given to the new one. So I can make shure,
1134 // they do not duplicate themself and you cannnot make dirty things with
1137 void LyXText::setParagraph(BufferView * bview,
1138 bool line_top, bool line_bottom,
1139 bool pagebreak_top, bool pagebreak_bottom,
1140 VSpace const & space_top,
1141 VSpace const & space_bottom,
1142 Spacing const & spacing,
1144 string labelwidthstring,
1147 LyXCursor tmpcursor = cursor;
1148 if (!selection.set()) {
1149 selection.start = cursor;
1150 selection.end = cursor;
1153 // make sure that the depth behind the selection are restored, too
1154 Paragraph * endpar = selection.end.par()->next();
1155 Paragraph * undoendpar = endpar;
1157 if (endpar && endpar->getDepth()) {
1158 while (endpar && endpar->getDepth()) {
1159 endpar = endpar->next();
1160 undoendpar = endpar;
1164 // because of parindents etc.
1165 endpar = endpar->next();
1168 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1171 Paragraph * tmppar = selection.end.par();
1173 while (tmppar != selection.start.par()->previous()) {
1174 setCursor(bview, tmppar, 0);
1175 status(bview, LyXText::NEED_MORE_REFRESH);
1176 refresh_row = cursor.row();
1177 refresh_y = cursor.y() - cursor.row()->baseline();
1178 cursor.par()->params().lineTop(line_top);
1179 cursor.par()->params().lineBottom(line_bottom);
1180 cursor.par()->params().pagebreakTop(pagebreak_top);
1181 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1182 cursor.par()->params().spaceTop(space_top);
1183 cursor.par()->params().spaceBottom(space_bottom);
1184 cursor.par()->params().spacing(spacing);
1185 // does the layout allow the new alignment?
1186 LyXLayout_ptr const & layout = cursor.par()->layout();
1188 if (align == LYX_ALIGN_LAYOUT)
1189 align = layout->align;
1190 if (align & layout->alignpossible) {
1191 if (align == layout->align)
1192 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1194 cursor.par()->params().align(align);
1196 cursor.par()->setLabelWidthString(labelwidthstring);
1197 cursor.par()->params().noindent(noindent);
1198 tmppar = cursor.par()->previous();
1201 redoParagraphs(bview, selection.start, endpar);
1204 setCursor(bview, selection.start.par(), selection.start.pos());
1205 selection.cursor = cursor;
1206 setCursor(bview, selection.end.par(), selection.end.pos());
1207 setSelection(bview);
1208 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1210 bview->updateInset(inset_owner, true);
1214 char loweralphaCounter(int n)
1216 if (n < 1 || n > 26)
1226 char alphaCounter(int n)
1228 if (n < 1 || n > 26)
1236 char hebrewCounter(int n)
1238 static const char hebrew[22] = {
1239 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1240 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1241 '÷', 'ø', 'ù', 'ú'
1243 if (n < 1 || n > 22)
1251 string const romanCounter(int n)
1253 static char const * roman[20] = {
1254 "i", "ii", "iii", "iv", "v",
1255 "vi", "vii", "viii", "ix", "x",
1256 "xi", "xii", "xiii", "xiv", "xv",
1257 "xvi", "xvii", "xviii", "xix", "xx"
1259 if (n < 1 || n > 20)
1268 // set the counter of a paragraph. This includes the labels
1269 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1271 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1272 LyXLayout_ptr const & layout = par->layout();
1274 // copy the prev-counters to this one,
1275 // unless this is the first paragraph
1276 if (par->previous()) {
1277 for (int i = 0; i < 10; ++i) {
1278 par->setCounter(i, par->previous()->getFirstCounter(i));
1280 par->params().appendix(par->previous()->params().appendix());
1281 if (!par->params().appendix() && par->params().startOfAppendix()) {
1282 par->params().appendix(true);
1283 for (int i = 0; i < 10; ++i) {
1284 par->setCounter(i, 0);
1287 par->enumdepth = par->previous()->enumdepth;
1288 par->itemdepth = par->previous()->itemdepth;
1290 for (int i = 0; i < 10; ++i) {
1291 par->setCounter(i, 0);
1293 par->params().appendix(par->params().startOfAppendix());
1298 /* Maybe we have to increment the enumeration depth.
1299 * BUT, enumeration in a footnote is considered in isolation from its
1300 * surrounding paragraph so don't increment if this is the
1301 * first line of the footnote
1302 * AND, bibliographies can't have their depth changed ie. they
1303 * are always of depth 0
1306 && par->previous()->getDepth() < par->getDepth()
1307 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1308 && par->enumdepth < 3
1309 && layout->labeltype != LABEL_BIBLIO) {
1313 // Maybe we have to decrement the enumeration depth, see note above
1315 && par->previous()->getDepth() > par->getDepth()
1316 && layout->labeltype != LABEL_BIBLIO) {
1317 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1318 par->setCounter(6 + par->enumdepth,
1319 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1320 // reset the counters.A depth change is like a breaking layout
1321 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1322 par->setCounter(i, 0);
1325 if (!par->params().labelString().empty()) {
1326 par->params().labelString(string());
1329 if (layout->margintype == MARGIN_MANUAL) {
1330 if (par->params().labelWidthString().empty()) {
1331 par->setLabelWidthString(layout->labelstring());
1334 par->setLabelWidthString(string());
1337 // is it a layout that has an automatic label?
1338 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1340 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1341 if (i >= 0 && i<= buf->params.secnumdepth) {
1342 par->incCounter(i); // increment the counter
1344 // Is there a label? Useful for Chapter layout
1345 if (!par->params().appendix()) {
1346 if (!layout->labelstring().empty())
1347 par->params().labelString(layout->labelstring());
1349 par->params().labelString(string());
1351 if (!layout->labelstring_appendix().empty())
1352 par->params().labelString(layout->labelstring_appendix());
1354 par->params().labelString(string());
1359 if (!par->params().appendix()) {
1360 switch (2 * LABEL_COUNTER_CHAPTER -
1361 textclass.maxcounter() + i) {
1362 case LABEL_COUNTER_CHAPTER:
1363 s << par->getCounter(i);
1365 case LABEL_COUNTER_SECTION:
1366 s << par->getCounter(i - 1) << '.'
1367 << par->getCounter(i);
1369 case LABEL_COUNTER_SUBSECTION:
1370 s << par->getCounter(i - 2) << '.'
1371 << par->getCounter(i - 1) << '.'
1372 << par->getCounter(i);
1374 case LABEL_COUNTER_SUBSUBSECTION:
1375 s << par->getCounter(i - 3) << '.'
1376 << par->getCounter(i - 2) << '.'
1377 << par->getCounter(i - 1) << '.'
1378 << par->getCounter(i);
1381 case LABEL_COUNTER_PARAGRAPH:
1382 s << par->getCounter(i - 4) << '.'
1383 << par->getCounter(i - 3) << '.'
1384 << par->getCounter(i - 2) << '.'
1385 << par->getCounter(i - 1) << '.'
1386 << par->getCounter(i);
1388 case LABEL_COUNTER_SUBPARAGRAPH:
1389 s << par->getCounter(i - 5) << '.'
1390 << par->getCounter(i - 4) << '.'
1391 << par->getCounter(i - 3) << '.'
1392 << par->getCounter(i - 2) << '.'
1393 << par->getCounter(i - 1) << '.'
1394 << par->getCounter(i);
1398 // Can this ever be reached? And in the
1399 // case it is, how can this be correct?
1401 s << par->getCounter(i) << '.';
1404 } else { // appendix
1405 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1406 case LABEL_COUNTER_CHAPTER:
1407 if (par->isRightToLeftPar(buf->params))
1408 s << hebrewCounter(par->getCounter(i));
1410 s << alphaCounter(par->getCounter(i));
1412 case LABEL_COUNTER_SECTION:
1413 if (par->isRightToLeftPar(buf->params))
1414 s << hebrewCounter(par->getCounter(i - 1));
1416 s << alphaCounter(par->getCounter(i - 1));
1419 << par->getCounter(i);
1422 case LABEL_COUNTER_SUBSECTION:
1423 if (par->isRightToLeftPar(buf->params))
1424 s << hebrewCounter(par->getCounter(i - 2));
1426 s << alphaCounter(par->getCounter(i - 2));
1429 << par->getCounter(i-1) << '.'
1430 << par->getCounter(i);
1433 case LABEL_COUNTER_SUBSUBSECTION:
1434 if (par->isRightToLeftPar(buf->params))
1435 s << hebrewCounter(par->getCounter(i-3));
1437 s << alphaCounter(par->getCounter(i-3));
1440 << par->getCounter(i-2) << '.'
1441 << par->getCounter(i-1) << '.'
1442 << par->getCounter(i);
1445 case LABEL_COUNTER_PARAGRAPH:
1446 if (par->isRightToLeftPar(buf->params))
1447 s << hebrewCounter(par->getCounter(i-4));
1449 s << alphaCounter(par->getCounter(i-4));
1452 << par->getCounter(i-3) << '.'
1453 << par->getCounter(i-2) << '.'
1454 << par->getCounter(i-1) << '.'
1455 << par->getCounter(i);
1458 case LABEL_COUNTER_SUBPARAGRAPH:
1459 if (par->isRightToLeftPar(buf->params))
1460 s << hebrewCounter(par->getCounter(i-5));
1462 s << alphaCounter(par->getCounter(i-5));
1465 << par->getCounter(i-4) << '.'
1466 << par->getCounter(i-3) << '.'
1467 << par->getCounter(i-2) << '.'
1468 << par->getCounter(i-1) << '.'
1469 << par->getCounter(i);
1473 // Can this ever be reached? And in the
1474 // case it is, how can this be correct?
1476 s << par->getCounter(i) << '.';
1482 par->params().labelString(par->params().labelString() +s.str().c_str());
1483 // We really want to remove the c_str as soon as
1486 for (i++; i < 10; ++i) {
1487 // reset the following counters
1488 par->setCounter(i, 0);
1490 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1491 for (i++; i < 10; ++i) {
1492 // reset the following counters
1493 par->setCounter(i, 0);
1495 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1496 par->incCounter(i + par->enumdepth);
1497 int number = par->getCounter(i + par->enumdepth);
1501 switch (par->enumdepth) {
1503 if (par->isRightToLeftPar(buf->params))
1505 << hebrewCounter(number)
1509 << loweralphaCounter(number)
1513 if (par->isRightToLeftPar(buf->params))
1514 s << '.' << romanCounter(number);
1516 s << romanCounter(number) << '.';
1519 if (par->isRightToLeftPar(buf->params))
1521 << alphaCounter(number);
1523 s << alphaCounter(number)
1527 if (par->isRightToLeftPar(buf->params))
1534 par->params().labelString(s.str().c_str());
1536 for (i += par->enumdepth + 1; i < 10; ++i) {
1537 // reset the following counters
1538 par->setCounter(i, 0);
1542 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1543 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1545 int number = par->getCounter(i);
1547 InsetCommandParams p("bibitem" );
1548 par->bibkey = new InsetBibKey(p);
1550 par->bibkey->setCounter(number);
1551 par->params().labelString(layout->labelstring());
1553 // In biblio should't be following counters but...
1555 string s = layout->labelstring();
1557 // the caption hack:
1558 if (layout->labeltype == LABEL_SENSITIVE) {
1559 bool isOK (par->inInset() && par->inInset()->owner() &&
1560 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1563 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1565 = floatList.getType(tmp->type());
1566 // We should get the correct number here too.
1567 s = fl.name() + " #:";
1569 /* par->SetLayout(0);
1570 s = layout->labelstring; */
1571 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1572 ? " :úåòîùî øñç" : "Senseless: ";
1575 par->params().labelString(s);
1577 /* reset the enumeration counter. They are always resetted
1578 * when there is any other layout between */
1579 for (int i = 6 + par->enumdepth; i < 10; ++i)
1580 par->setCounter(i, 0);
1585 // Updates all counters BEHIND the row. Changed paragraphs
1586 // with a dynamic left margin will be rebroken.
1587 void LyXText::updateCounters(BufferView * bview, Row * row) const
1595 par = row->par()->next();
1599 while (row->par() != par)
1602 setCounter(bview->buffer(), par);
1604 // now check for the headline layouts. remember that they
1605 // have a dynamic left margin
1606 LyXLayout_ptr const & layout = par->layout();
1608 if (layout->margintype == MARGIN_DYNAMIC
1609 || layout->labeltype == LABEL_SENSITIVE) {
1610 // Rebreak the paragraph
1611 removeParagraph(row);
1612 appendParagraph(bview, row);
1619 void LyXText::insertInset(BufferView * bview, Inset * inset)
1621 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1623 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1625 cursor.par()->insertInset(cursor.pos(), inset);
1626 // Just to rebreak and refresh correctly.
1627 // The character will not be inserted a second time
1628 insertChar(bview, Paragraph::META_INSET);
1629 // If we enter a highly editable inset the cursor should be to before
1630 // the inset. This couldn't happen before as Undo was not handled inside
1631 // inset now after the Undo LyX tries to call inset->Edit(...) again
1632 // and cannot do this as the cursor is behind the inset and GetInset
1633 // does not return the inset!
1634 if (isHighlyEditableInset(inset)) {
1635 cursorLeft(bview, true);
1641 void LyXText::copyEnvironmentType()
1643 copylayouttype = cursor.par()->layout()->name();
1647 void LyXText::pasteEnvironmentType(BufferView * bview)
1649 setLayout(bview, copylayouttype);
1653 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1655 // Stuff what we got on the clipboard. Even if there is no selection.
1657 // There is a problem with having the stuffing here in that the
1658 // larger the selection the slower LyX will get. This can be
1659 // solved by running the line below only when the selection has
1660 // finished. The solution used currently just works, to make it
1661 // faster we need to be more clever and probably also have more
1662 // calls to stuffClipboard. (Lgb)
1663 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1665 // This doesn't make sense, if there is no selection
1666 if (!selection.set())
1669 // OK, we have a selection. This is always between selection.start
1670 // and selection.end
1672 // make sure that the depth behind the selection are restored, too
1673 Paragraph * endpar = selection.end.par()->next();
1674 Paragraph * undoendpar = endpar;
1676 if (endpar && endpar->getDepth()) {
1677 while (endpar && endpar->getDepth()) {
1678 endpar = endpar->next();
1679 undoendpar = endpar;
1681 } else if (endpar) {
1682 endpar = endpar->next(); // because of parindents etc.
1685 setUndo(bview, Undo::DELETE,
1686 selection.start.par(), undoendpar);
1688 // there are two cases: cut only within one paragraph or
1689 // more than one paragraph
1690 if (selection.start.par() == selection.end.par()) {
1691 // only within one paragraph
1692 endpar = selection.end.par();
1693 int pos = selection.end.pos();
1694 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1695 selection.start.pos(), pos,
1696 bview->buffer()->params.textclass,
1698 selection.end.pos(pos);
1700 endpar = selection.end.par();
1701 int pos = selection.end.pos();
1702 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1703 selection.start.pos(), pos,
1704 bview->buffer()->params.textclass,
1707 selection.end.par(endpar);
1708 selection.end.pos(pos);
1709 cursor.pos(selection.end.pos());
1711 endpar = endpar->next();
1713 // sometimes necessary
1715 selection.start.par()->stripLeadingSpaces();
1717 redoParagraphs(bview, selection.start, endpar);
1719 // cutSelection can invalidate the cursor so we need to set
1721 cursor = selection.start;
1723 // need a valid cursor. (Lgb)
1726 setCursor(bview, cursor.par(), cursor.pos());
1727 selection.cursor = cursor;
1728 updateCounters(bview, cursor.row());
1732 void LyXText::copySelection(BufferView * bview)
1734 // stuff the selection onto the X clipboard, from an explicit copy request
1735 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1737 // this doesnt make sense, if there is no selection
1738 if (!selection.set())
1741 // ok we have a selection. This is always between selection.start
1742 // and sel_end cursor
1744 // copy behind a space if there is one
1745 while (selection.start.par()->size() > selection.start.pos()
1746 && selection.start.par()->isLineSeparator(selection.start.pos())
1747 && (selection.start.par() != selection.end.par()
1748 || selection.start.pos() < selection.end.pos()))
1749 selection.start.pos(selection.start.pos() + 1);
1751 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1752 selection.start.pos(), selection.end.pos(),
1753 bview->buffer()->params.textclass);
1757 void LyXText::pasteSelection(BufferView * bview)
1759 // this does not make sense, if there is nothing to paste
1760 if (!CutAndPaste::checkPastePossible(cursor.par()))
1763 setUndo(bview, Undo::INSERT,
1764 cursor.par(), cursor.par()->next());
1767 Paragraph * actpar = cursor.par();
1768 int pos = cursor.pos();
1770 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1771 bview->buffer()->params.textclass);
1773 redoParagraphs(bview, cursor, endpar);
1775 setCursor(bview, cursor.par(), cursor.pos());
1778 selection.cursor = cursor;
1779 setCursor(bview, actpar, pos);
1780 setSelection(bview);
1781 updateCounters(bview, cursor.row());
1785 // sets the selection over the number of characters of string, no check!!
1786 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1791 selection.cursor = cursor;
1792 for (string::size_type i = 0; i < str.length(); ++i)
1794 setSelection(bview);
1798 // simple replacing. The font of the first selected character is used
1799 void LyXText::replaceSelectionWithString(BufferView * bview,
1802 setCursorParUndo(bview);
1805 if (!selection.set()) { // create a dummy selection
1806 selection.end = cursor;
1807 selection.start = cursor;
1810 // Get font setting before we cut
1811 pos_type pos = selection.end.pos();
1812 LyXFont const font = selection.start.par()
1813 ->getFontSettings(bview->buffer()->params,
1814 selection.start.pos());
1816 // Insert the new string
1817 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1818 selection.end.par()->insertChar(pos, (*cit), font);
1822 // Cut the selection
1823 cutSelection(bview, true, false);
1829 // needed to insert the selection
1830 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1832 Paragraph * par = cursor.par();
1833 pos_type pos = cursor.pos();
1834 Paragraph * endpar = cursor.par()->next();
1836 setCursorParUndo(bview);
1838 // only to be sure, should not be neccessary
1841 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1843 redoParagraphs(bview, cursor, endpar);
1844 setCursor(bview, cursor.par(), cursor.pos());
1845 selection.cursor = cursor;
1846 setCursor(bview, par, pos);
1847 setSelection(bview);
1851 // turns double-CR to single CR, others where converted into one
1852 // blank. Then InsertStringAsLines is called
1853 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1855 string linestr(str);
1856 bool newline_inserted = false;
1857 for (string::size_type i = 0; i < linestr.length(); ++i) {
1858 if (linestr[i] == '\n') {
1859 if (newline_inserted) {
1860 // we know that \r will be ignored by
1861 // InsertStringA. Of course, it is a dirty
1862 // trick, but it works...
1863 linestr[i - 1] = '\r';
1867 newline_inserted = true;
1869 } else if (IsPrintable(linestr[i])) {
1870 newline_inserted = false;
1873 insertStringAsLines(bview, linestr);
1877 bool LyXText::gotoNextInset(BufferView * bview,
1878 vector<Inset::Code> const & codes,
1879 string const & contents) const
1881 LyXCursor res = cursor;
1884 if (res.pos() < res.par()->size() - 1) {
1885 res.pos(res.pos() + 1);
1887 res.par(res.par()->next());
1891 } while (res.par() &&
1892 !(res.par()->isInset(res.pos())
1893 && (inset = res.par()->getInset(res.pos())) != 0
1894 && find(codes.begin(), codes.end(), inset->lyxCode())
1896 && (contents.empty() ||
1897 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1901 setCursor(bview, res.par(), res.pos(), false);
1908 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1911 LyXCursor tmpcursor;
1915 Row * row = getRow(par, pos, y);
1917 // is there a break one row above
1918 if (row->previous() && row->previous()->par() == row->par()) {
1919 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1920 if (z >= row->pos()) {
1921 // set the dimensions of the row above
1922 y -= row->previous()->height();
1924 refresh_row = row->previous();
1925 status(bview, LyXText::NEED_MORE_REFRESH);
1927 breakAgain(bview, row->previous());
1929 // set the cursor again. Otherwise
1930 // dangling pointers are possible
1931 setCursor(bview, cursor.par(), cursor.pos(),
1932 false, cursor.boundary());
1933 selection.cursor = cursor;
1938 int const tmpheight = row->height();
1939 pos_type const tmplast = rowLast(row);
1943 breakAgain(bview, row);
1944 if (row->height() == tmpheight && rowLast(row) == tmplast)
1945 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1947 status(bview, LyXText::NEED_MORE_REFRESH);
1949 // check the special right address boxes
1950 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1957 redoDrawingOfParagraph(bview, tmpcursor);
1960 // set the cursor again. Otherwise dangling pointers are possible
1961 // also set the selection
1963 if (selection.set()) {
1965 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1966 false, selection.cursor.boundary());
1967 selection.cursor = cursor;
1968 setCursorIntern(bview, selection.start.par(),
1969 selection.start.pos(),
1970 false, selection.start.boundary());
1971 selection.start = cursor;
1972 setCursorIntern(bview, selection.end.par(),
1973 selection.end.pos(),
1974 false, selection.end.boundary());
1975 selection.end = cursor;
1976 setCursorIntern(bview, last_sel_cursor.par(),
1977 last_sel_cursor.pos(),
1978 false, last_sel_cursor.boundary());
1979 last_sel_cursor = cursor;
1982 setCursorIntern(bview, cursor.par(), cursor.pos(),
1983 false, cursor.boundary());
1987 // returns false if inset wasn't found
1988 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1990 // first check the current paragraph
1991 int pos = cursor.par()->getPositionOfInset(inset);
1993 checkParagraph(bview, cursor.par(), pos);
1997 // check every paragraph
1999 Paragraph * par = ownerParagraph();
2001 pos = par->getPositionOfInset(inset);
2003 checkParagraph(bview, par, pos);
2013 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2015 bool setfont, bool boundary) const
2017 LyXCursor old_cursor = cursor;
2018 setCursorIntern(bview, par, pos, setfont, boundary);
2019 return deleteEmptyParagraphMechanism(bview, old_cursor);
2023 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2024 pos_type pos, bool boundary) const
2031 cur.boundary(boundary);
2033 // get the cursor y position in text
2035 Row * row = getRow(par, pos, y);
2036 Row * old_row = row;
2038 // if we are before the first char of this row and are still in the
2039 // same paragraph and there is a previous row then put the cursor on
2040 // the end of the previous row
2041 cur.iy(y + row->baseline());
2043 if (row->previous() && pos &&
2044 row->previous()->par() == row->par() &&
2045 par->getChar(pos) == Paragraph::META_INSET &&
2046 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2048 row = row->previous();
2053 // y is now the beginning of the cursor row
2054 y += row->baseline();
2055 // y is now the cursor baseline
2058 pos_type last = rowLastPrintable(old_row);
2060 if (pos > last + 1) {
2061 // This shouldn't happen.
2064 } else if (pos < row->pos()) {
2069 // now get the cursors x position
2070 float x = getCursorX(bview, row, pos, last, boundary);
2073 if (old_row != row) {
2074 x = getCursorX(bview, old_row, pos, last, boundary);
2081 float LyXText::getCursorX(BufferView * bview, Row * row,
2082 pos_type pos, pos_type last, bool boundary) const
2084 pos_type cursor_vpos = 0;
2086 float fill_separator;
2088 float fill_label_hfill;
2089 // This call HAS to be here because of the BidiTables!!!
2090 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2093 if (last < row->pos())
2094 cursor_vpos = row->pos();
2095 else if (pos > last && !boundary)
2096 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2097 ? row->pos() : last + 1;
2098 else if (pos > row->pos() &&
2099 (pos > last || boundary))
2100 /// Place cursor after char at (logical) position pos - 1
2101 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2102 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2104 /// Place cursor before char at (logical) position pos
2105 cursor_vpos = (bidi_level(pos) % 2 == 0)
2106 ? log2vis(pos) : log2vis(pos) + 1;
2108 pos_type main_body =
2109 beginningOfMainBody(bview->buffer(), row->par());
2110 if ((main_body > 0) &&
2111 ((main_body-1 > last) ||
2112 !row->par()->isLineSeparator(main_body-1)))
2115 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2116 pos_type pos = vis2log(vpos);
2117 if (main_body > 0 && pos == main_body - 1) {
2118 x += fill_label_hfill +
2119 font_metrics::width(
2120 row->par()->layout()->labelsep,
2121 getLabelFont(bview->buffer(),
2123 if (row->par()->isLineSeparator(main_body - 1))
2124 x -= singleWidth(bview,
2125 row->par(), main_body - 1);
2127 if (hfillExpansion(bview->buffer(), row, pos)) {
2128 x += singleWidth(bview, row->par(), pos);
2129 if (pos >= main_body)
2132 x += fill_label_hfill;
2133 } else if (row->par()->isSeparator(pos)) {
2134 x += singleWidth(bview, row->par(), pos);
2135 if (pos >= main_body)
2136 x += fill_separator;
2138 x += singleWidth(bview, row->par(), pos);
2144 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2145 pos_type pos, bool setfont, bool boundary) const
2147 InsetText * it = static_cast<InsetText *>(par->inInset());
2149 if (it != inset_owner) {
2150 lyxerr[Debug::INSETS] << "InsetText is " << it
2152 << "inset_owner is "
2153 << inset_owner << endl;
2154 #ifdef WITH_WARNINGS
2155 #warning I believe this code is wrong. (Lgb)
2156 #warning Jürgen, have a look at this. (Lgb)
2157 #warning Hmmm, I guess you are right but we
2158 #warning should verify when this is needed
2160 // Jürgen, would you like to have a look?
2161 // I guess we need to move the outer cursor
2162 // and open and lock the inset (bla bla bla)
2163 // stuff I don't know... so can you have a look?
2165 // I moved the lyxerr stuff in here so we can see if
2166 // this is actually really needed and where!
2168 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2173 setCursor(bview, cursor, par, pos, boundary);
2175 setCurrentFont(bview);
2179 void LyXText::setCurrentFont(BufferView * bview) const
2181 pos_type pos = cursor.pos();
2182 if (cursor.boundary() && pos > 0)
2186 if (pos == cursor.par()->size())
2188 else // potentional bug... BUG (Lgb)
2189 if (cursor.par()->isSeparator(pos)) {
2190 if (pos > cursor.row()->pos() &&
2191 bidi_level(pos) % 2 ==
2192 bidi_level(pos - 1) % 2)
2194 else if (pos + 1 < cursor.par()->size())
2200 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2201 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2203 if (cursor.pos() == cursor.par()->size() &&
2204 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2205 !cursor.boundary()) {
2206 Language const * lang =
2207 cursor.par()->getParLanguage(bview->buffer()->params);
2208 current_font.setLanguage(lang);
2209 current_font.setNumber(LyXFont::OFF);
2210 real_current_font.setLanguage(lang);
2211 real_current_font.setNumber(LyXFont::OFF);
2216 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2218 LyXCursor old_cursor = cursor;
2220 setCursorFromCoordinates(bview, cursor, x, y);
2221 setCurrentFont(bview);
2222 deleteEmptyParagraphMechanism(bview, old_cursor);
2229 * return true if the cursor given is at the end of a row,
2230 * and the next row is filled by an inset that spans an entire
2233 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2236 Row const & next = *row.next();
2238 if (next.pos() != cur.pos() || next.par() != cur.par())
2240 if (!cur.par()->isInset(cur.pos()))
2242 Inset const * inset = cur.par()->getInset(cur.pos());
2243 if (inset->needFullRow() || inset->display())
2250 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2253 // Get the row first.
2255 Row * row = getRowNearY(y);
2257 pos_type const column = getColumnNearX(bview, row, x, bound);
2258 cur.par(row->par());
2259 cur.pos(row->pos() + column);
2261 cur.y(y + row->baseline());
2264 if (beforeFullRowInset(*row, cur)) {
2265 pos_type last = rowLastPrintable(row);
2266 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2268 cur.iy(y + row->height() + row->next()->baseline());
2269 cur.irow(row->next());
2275 cur.boundary(bound);
2279 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2281 if (cursor.pos() > 0) {
2282 bool boundary = cursor.boundary();
2283 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2284 if (!internal && !boundary &&
2285 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2286 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2287 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2288 Paragraph * par = cursor.par()->previous();
2289 setCursor(bview, par, par->size());
2294 void LyXText::cursorRight(BufferView * bview, bool internal) const
2296 if (!internal && cursor.boundary() &&
2297 !cursor.par()->isNewline(cursor.pos()))
2298 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2299 else if (cursor.pos() < cursor.par()->size()) {
2300 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2302 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2303 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2304 } else if (cursor.par()->next())
2305 setCursor(bview, cursor.par()->next(), 0);
2309 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2312 int x = cursor.x_fix();
2313 int y = cursor.y() - cursor.row()->baseline() - 1;
2314 setCursorFromCoordinates(bview, x, y);
2316 int y1 = cursor.iy() - first_y;
2320 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2321 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2322 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2326 setCursorFromCoordinates(bview, cursor.x_fix(),
2327 cursor.y() - cursor.row()->baseline() - 1);
2332 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2335 int x = cursor.x_fix();
2336 int y = cursor.y() - cursor.row()->baseline() +
2337 cursor.row()->height() + 1;
2338 setCursorFromCoordinates(bview, x, y);
2339 if (!selecting && cursor.row() == cursor.irow()) {
2340 int y1 = cursor.iy() - first_y;
2344 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2345 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2346 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2350 setCursorFromCoordinates(bview, cursor.x_fix(),
2351 cursor.y() - cursor.row()->baseline()
2352 + cursor.row()->height() + 1);
2357 void LyXText::cursorUpParagraph(BufferView * bview) const
2359 if (cursor.pos() > 0) {
2360 setCursor(bview, cursor.par(), 0);
2362 else if (cursor.par()->previous()) {
2363 setCursor(bview, cursor.par()->previous(), 0);
2368 void LyXText::cursorDownParagraph(BufferView * bview) const
2370 if (cursor.par()->next()) {
2371 setCursor(bview, cursor.par()->next(), 0);
2373 setCursor(bview, cursor.par(), cursor.par()->size());
2377 // fix the cursor `cur' after a characters has been deleted at `where'
2378 // position. Called by deleteEmptyParagraphMechanism
2379 void LyXText::fixCursorAfterDelete(BufferView * bview,
2381 LyXCursor const & where) const
2383 // if cursor is not in the paragraph where the delete occured,
2385 if (cur.par() != where.par())
2388 // if cursor position is after the place where the delete occured,
2390 if (cur.pos() > where.pos())
2391 cur.pos(cur.pos()-1);
2393 // check also if we don't want to set the cursor on a spot behind the
2394 // pagragraph because we erased the last character.
2395 if (cur.pos() > cur.par()->size())
2396 cur.pos(cur.par()->size());
2398 // recompute row et al. for this cursor
2399 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2403 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2404 LyXCursor const & old_cursor) const
2406 // Would be wrong to delete anything if we have a selection.
2407 if (selection.set())
2410 // We allow all kinds of "mumbo-jumbo" when freespacing.
2411 if (old_cursor.par()->layout()->free_spacing
2412 || old_cursor.par()->isFreeSpacing()) {
2416 /* Ok I'll put some comments here about what is missing.
2417 I have fixed BackSpace (and thus Delete) to not delete
2418 double-spaces automagically. I have also changed Cut,
2419 Copy and Paste to hopefully do some sensible things.
2420 There are still some small problems that can lead to
2421 double spaces stored in the document file or space at
2422 the beginning of paragraphs. This happens if you have
2423 the cursor betwenn to spaces and then save. Or if you
2424 cut and paste and the selection have a space at the
2425 beginning and then save right after the paste. I am
2426 sure none of these are very hard to fix, but I will
2427 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2428 that I can get some feedback. (Lgb)
2431 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2432 // delete the LineSeparator.
2435 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2436 // delete the LineSeparator.
2439 // If the pos around the old_cursor were spaces, delete one of them.
2440 if (old_cursor.par() != cursor.par()
2441 || old_cursor.pos() != cursor.pos()) {
2442 // Only if the cursor has really moved
2444 if (old_cursor.pos() > 0
2445 && old_cursor.pos() < old_cursor.par()->size()
2446 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2447 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2448 old_cursor.par()->erase(old_cursor.pos() - 1);
2449 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2451 #ifdef WITH_WARNINGS
2452 #warning This will not work anymore when we have multiple views of the same buffer
2453 // In this case, we will have to correct also the cursors held by
2454 // other bufferviews. It will probably be easier to do that in a more
2455 // automated way in LyXCursor code. (JMarc 26/09/2001)
2457 // correct all cursors held by the LyXText
2458 fixCursorAfterDelete(bview, cursor, old_cursor);
2459 fixCursorAfterDelete(bview, selection.cursor,
2461 fixCursorAfterDelete(bview, selection.start,
2463 fixCursorAfterDelete(bview, selection.end, old_cursor);
2464 fixCursorAfterDelete(bview, last_sel_cursor,
2466 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2467 fixCursorAfterDelete(bview, toggle_end_cursor,
2473 // don't delete anything if this is the ONLY paragraph!
2474 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2477 // Do not delete empty paragraphs with keepempty set.
2478 if (old_cursor.par()->layout()->keepempty)
2481 // only do our magic if we changed paragraph
2482 if (old_cursor.par() == cursor.par())
2485 // record if we have deleted a paragraph
2486 // we can't possibly have deleted a paragraph before this point
2487 bool deleted = false;
2489 if ((old_cursor.par()->size() == 0
2490 || (old_cursor.par()->size() == 1
2491 && old_cursor.par()->isLineSeparator(0)))) {
2492 // ok, we will delete anything
2493 LyXCursor tmpcursor;
2495 // make sure that you do not delete any environments
2496 status(bview, LyXText::NEED_MORE_REFRESH);
2499 if (old_cursor.row()->previous()) {
2500 refresh_row = old_cursor.row()->previous();
2501 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2503 cursor = old_cursor; // that undo can restore the right cursor position
2504 Paragraph * endpar = old_cursor.par()->next();
2505 if (endpar && endpar->getDepth()) {
2506 while (endpar && endpar->getDepth()) {
2507 endpar = endpar->next();
2510 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2514 removeRow(old_cursor.row());
2515 if (ownerParagraph() == old_cursor.par()) {
2516 ownerParagraph(ownerParagraph()->next());
2519 delete old_cursor.par();
2521 /* Breakagain the next par. Needed because of
2522 * the parindent that can occur or dissappear.
2523 * The next row can change its height, if
2524 * there is another layout before */
2525 if (refresh_row->next()) {
2526 breakAgain(bview, refresh_row->next());
2527 updateCounters(bview, refresh_row);
2529 setHeightOfRow(bview, refresh_row);
2531 refresh_row = old_cursor.row()->next();
2532 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2535 cursor = old_cursor; // that undo can restore the right cursor position
2536 Paragraph * endpar = old_cursor.par()->next();
2537 if (endpar && endpar->getDepth()) {
2538 while (endpar && endpar->getDepth()) {
2539 endpar = endpar->next();
2542 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2546 removeRow(old_cursor.row());
2548 if (ownerParagraph() == old_cursor.par()) {
2549 ownerParagraph(ownerParagraph()->next());
2552 delete old_cursor.par();
2554 /* Breakagain the next par. Needed because of
2555 the parindent that can occur or dissappear.
2556 The next row can change its height, if
2557 there is another layout before */
2559 breakAgain(bview, refresh_row);
2560 updateCounters(bview, refresh_row->previous());
2565 setCursorIntern(bview, cursor.par(), cursor.pos());
2567 if (selection.cursor.par() == old_cursor.par()
2568 && selection.cursor.pos() == old_cursor.pos()) {
2569 // correct selection
2570 selection.cursor = cursor;
2574 if (old_cursor.par()->stripLeadingSpaces()) {
2575 redoParagraphs(bview, old_cursor,
2576 old_cursor.par()->next());
2578 setCursorIntern(bview, cursor.par(), cursor.pos());
2579 selection.cursor = cursor;
2586 void LyXText::toggleAppendix(BufferView * bview)
2588 Paragraph * par = cursor.par();
2589 bool start = !par->params().startOfAppendix();
2591 // ensure that we have only one start_of_appendix in this document
2592 Paragraph * tmp = ownerParagraph();
2593 for (; tmp; tmp = tmp->next()) {
2594 tmp->params().startOfAppendix(false);
2597 par->params().startOfAppendix(start);
2599 // we can set the refreshing parameters now
2600 status(bview, LyXText::NEED_MORE_REFRESH);
2602 refresh_row = 0; // not needed for full update
2603 updateCounters(bview, 0);
2604 setCursor(bview, cursor.par(), cursor.pos());
2608 Paragraph * LyXText::ownerParagraph() const
2611 return inset_owner->paragraph();
2613 return bv_owner->buffer()->paragraph;
2617 void LyXText::ownerParagraph(Paragraph * p) const
2620 inset_owner->paragraph(p);
2622 bv_owner->buffer()->paragraph = p;
2627 void LyXText::ownerParagraph(int id, Paragraph * p) const
2629 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2630 if (op && op->inInset()) {
2631 static_cast<InsetText *>(op->inInset())->paragraph(p);
2638 LyXText::text_status LyXText::status() const
2644 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2646 LyXText * t = bview->text;
2648 // We should only go up with refreshing code so this means that if
2649 // we have a MORE refresh we should never set it to LITTLE if we still
2650 // didn't handle it (and then it will be UNCHANGED. Now as long as
2651 // we stay inside one LyXText this may work but we need to tell the
2652 // outermost LyXText that it should REALLY draw us if there is some
2653 // change in a Inset::LyXText. So you see that when we are inside a
2654 // inset's LyXText we give the LITTLE to the outermost LyXText to
2655 // tell'em that it should redraw the actual row (where the inset
2656 // resides! Capito?!
2658 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2660 if (inset_owner && st != UNCHANGED) {
2661 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2662 if (!t->refresh_row) {
2663 t->refresh_row = t->cursor.row();
2664 t->refresh_y = t->cursor.y() -
2665 t->cursor.row()->baseline();