1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "lyxtextclasslist.h"
21 #include "frontends/LyXView.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
27 #include "CutAndPaste.h"
28 #include "frontends/Painter.h"
29 #include "frontends/font_metrics.h"
33 #include "FloatList.h"
35 #include "ParagraphParameters.h"
37 #include "insets/inseterror.h"
38 #include "insets/insetbib.h"
39 #include "insets/insetspecialchar.h"
40 #include "insets/insettext.h"
41 #include "insets/insetfloat.h"
43 #include "support/LAssert.h"
44 #include "support/textutils.h"
45 #include "support/lstrings.h"
56 LyXText::LyXText(BufferView * bv)
57 : number_of_rows(0), height(0), width(0), first_y(0),
58 bv_owner(bv), inset_owner(0), the_locking_inset(0),
59 need_break_row(0), refresh_y(0), refresh_row(0),
60 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
64 LyXText::LyXText(InsetText * inset)
65 : number_of_rows(0), height(0), width(0), first_y(0),
66 bv_owner(0), inset_owner(inset), the_locking_inset(0),
67 need_break_row(0), refresh_y(0), refresh_row(0),
68 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
72 void LyXText::init(BufferView * bview, bool reinit)
75 // Delete all rows, this does not touch the paragraphs!
76 Row * tmprow = firstrow;
78 tmprow = firstrow->next();
87 copylayouttype.erase();
88 number_of_rows = first_y = refresh_y = 0;
89 status_ = LyXText::UNCHANGED;
93 Paragraph * par = ownerParagraph();
94 current_font = getFont(bview->buffer(), par, 0);
97 insertParagraph(bview, par, lastrow);
100 setCursorIntern(bview, firstrow->par(), 0);
101 selection.cursor = cursor;
107 // Delete all rows, this does not touch the paragraphs!
108 Row * tmprow = firstrow;
110 tmprow = firstrow->next();
119 LyXFont const realizeFont(LyXFont const & font,
123 LyXFont tmpfont(font);
124 Paragraph::depth_type par_depth = par->getDepth();
126 // Resolve against environment font information
127 while (par && par_depth && !tmpfont.resolved()) {
128 par = par->outerHook();
130 #ifndef INHERIT_LANGUAGE
131 tmpfont.realize(par->layout()->font);
133 tmpfont.realize(textclasslist.
134 Style(buf->params.textclass,
135 par->layout())->font,
136 buf->params.language);
138 par_depth = par->getDepth();
142 #ifndef INHERIT_LANGUAGE
143 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
145 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
146 buf->params.language);
155 // Gets the fully instantiated font at a given position in a paragraph
156 // Basically the same routine as Paragraph::getFont() in paragraph.C.
157 // The difference is that this one is used for displaying, and thus we
158 // are allowed to make cosmetic improvements. For instance make footnotes
160 // If position is -1, we get the layout font of the paragraph.
161 // If position is -2, we get the font of the manual label of the paragraph.
162 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
165 lyx::Assert(pos >= 0);
167 LyXLayout_ptr const & layout = par->layout();
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout->labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params, pos);
177 par->inInset()->getDrawFont(f);
178 #ifndef INHERIT_LANGUAGE
179 return f.realize(layout->reslabelfont);
181 return f.realize(layout.reslabelfont, buf->params.language);
184 LyXFont f = par->getFontSettings(buf->params, pos);
186 par->inInset()->getDrawFont(f);
187 #ifndef INHERIT_LANGUAGE
188 return f.realize(layout->resfont);
190 return f.realize(layout.resfont, buf->params.language);
195 // The uncommon case need not be optimized as much
199 if (pos < beginningOfMainBody(buf, par)) {
201 layoutfont = layout->labelfont;
204 layoutfont = layout->font;
207 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
208 #ifndef INHERIT_LANGUAGE
209 tmpfont.realize(layoutfont);
211 tmpfont.realize(layoutfont, buf->params.language);
214 par->inInset()->getDrawFont(tmpfont);
216 return realizeFont(tmpfont, buf, par);
220 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
222 LyXLayout_ptr const & layout = par->layout();
224 Paragraph::depth_type par_depth = par->getDepth();
227 return layout->resfont;
230 return realizeFont(layout->font, buf, par);
234 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
236 LyXLayout_ptr const & layout = par->layout();
238 Paragraph::depth_type par_depth = par->getDepth();
241 return layout->reslabelfont;
244 return realizeFont(layout->labelfont, buf, par);
248 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
249 pos_type pos, LyXFont const & fnt,
252 Buffer const * buf = bv->buffer();
253 LyXFont font = getFont(buf, par, pos);
254 font.update(fnt, buf->params.language, toggleall);
255 // Let the insets convert their font
256 if (par->isInset(pos)) {
257 Inset * inset = par->getInset(pos);
258 if (isEditableInset(inset)) {
259 UpdatableInset * uinset =
260 static_cast<UpdatableInset *>(inset);
261 uinset->setFont(bv, fnt, toggleall, true);
265 // Plug thru to version below:
266 setCharFont(buf, par, pos, font);
270 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
271 pos_type pos, LyXFont const & fnt)
275 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
276 LyXLayout_ptr const & layout = par->layout();
278 // Get concrete layout font to reduce against
281 if (pos < beginningOfMainBody(buf, par))
282 layoutfont = layout->labelfont;
284 layoutfont = layout->font;
286 // Realize against environment font information
287 if (par->getDepth()) {
288 Paragraph * tp = par;
289 while (!layoutfont.resolved() && tp && tp->getDepth()) {
290 tp = tp->outerHook();
292 #ifndef INHERIT_LANGUAGE
293 layoutfont.realize(tp->layout()->font);
295 layoutfont.realize(textclasslist.
296 Style(buf->params.textclass,
298 buf->params.language);
303 #ifndef INHERIT_LANGUAGE
304 layoutfont.realize(tclass.defaultfont());
306 layoutfont.realize(tclass.defaultfont(), buf->params.language);
309 // Now, reduce font against full layout font
310 font.reduce(layoutfont);
312 par->setFont(pos, font);
316 // inserts a new row behind the specified row, increments
317 // the touched counters
318 void LyXText::insertRow(Row * row, Paragraph * par,
321 Row * tmprow = new Row;
324 tmprow->next(firstrow);
327 tmprow->previous(row);
328 tmprow->next(row->next());
333 tmprow->next()->previous(tmprow);
335 if (tmprow->previous())
336 tmprow->previous()->next(tmprow);
348 // removes the row and reset the touched counters
349 void LyXText::removeRow(Row * row) const
351 Row * row_prev = row->previous();
353 row->next()->previous(row_prev);
355 firstrow = row->next();
356 // lyx::Assert(firstrow);
358 row_prev->next(row->next());
360 if (row == lastrow) {
361 lyx::Assert(!row->next());
364 if (refresh_row == row) {
365 refresh_row = row_prev ? row_prev : row->next();
366 // what about refresh_y, refresh_height
369 height -= row->height(); // the text becomes smaller
372 --number_of_rows; // one row less
376 // remove all following rows of the paragraph of the specified row.
377 void LyXText::removeParagraph(Row * row) const
379 Paragraph * tmppar = row->par();
383 while (row && row->par() == tmppar) {
384 tmprow = row->next();
391 // insert the specified paragraph behind the specified row
392 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
395 insertRow(row, par, 0); /* insert a new row, starting
398 setCounter(bview->buffer(), par); // set the counters
400 // and now append the whole paragraph behind the new row
403 appendParagraph(bview, firstrow);
405 row->next()->height(0);
406 appendParagraph(bview, row->next());
411 Inset * LyXText::getInset() const
414 if (cursor.pos() == 0 && cursor.par()->bibkey) {
415 inset = cursor.par()->bibkey;
416 } else if (cursor.pos() < cursor.par()->size()
417 && cursor.par()->isInset(cursor.pos())) {
418 inset = cursor.par()->getInset(cursor.pos());
424 void LyXText::toggleInset(BufferView * bview)
426 Inset * inset = getInset();
427 // is there an editable inset at cursor position?
428 if (!isEditableInset(inset)) {
429 // No, try to see if we are inside a collapsable inset
430 if (inset_owner && inset_owner->owner()
431 && inset_owner->owner()->isOpen()) {
432 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
433 inset_owner->owner()->close(bview);
434 bview->getLyXText()->cursorRight(bview);
438 //bview->owner()->message(inset->editMessage());
440 // do we want to keep this?? (JMarc)
441 if (!isHighlyEditableInset(inset))
442 setCursorParUndo(bview);
444 if (inset->isOpen()) {
450 inset->open(bview, !inset->isOpen());
455 /* used in setlayout */
456 // Asger is not sure we want to do this...
457 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
460 LyXLayout_ptr const & layout = par->layout();
463 for (pos_type pos = 0; pos < par->size(); ++pos) {
464 if (pos < beginningOfMainBody(buf, par))
465 layoutfont = layout->labelfont;
467 layoutfont = layout->font;
469 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
470 tmpfont.reduce(layoutfont);
471 par->setFont(pos, tmpfont);
476 Paragraph * LyXText::setLayout(BufferView * bview,
477 LyXCursor & cur, LyXCursor & sstart_cur,
478 LyXCursor & send_cur,
479 string const & layout)
481 Paragraph * endpar = send_cur.par()->next();
482 Paragraph * undoendpar = endpar;
484 if (endpar && endpar->getDepth()) {
485 while (endpar && endpar->getDepth()) {
486 endpar = endpar->next();
490 endpar = endpar->next(); // because of parindents etc.
493 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
495 // ok we have a selection. This is always between sstart_cur
496 // and sel_end cursor
498 Paragraph * par = sstart_cur.par();
499 Paragraph * epar = send_cur.par()->next();
501 LyXLayout_ptr const & lyxlayout =
502 textclasslist[bview->buffer()->params.textclass][layout];
505 par->applyLayout(lyxlayout);
506 makeFontEntriesLayoutSpecific(bview->buffer(), par);
507 Paragraph * fppar = par;
508 fppar->params().spaceTop(lyxlayout->fill_top ?
509 VSpace(VSpace::VFILL)
510 : VSpace(VSpace::NONE));
511 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
512 VSpace(VSpace::VFILL)
513 : VSpace(VSpace::NONE));
514 if (lyxlayout->margintype == MARGIN_MANUAL)
515 par->setLabelWidthString(lyxlayout->labelstring());
516 if (lyxlayout->labeltype != LABEL_BIBLIO
518 delete fppar->bibkey;
523 } while (par != epar);
529 // set layout over selection and make a total rebreak of those paragraphs
530 void LyXText::setLayout(BufferView * bview, string const & layout)
532 LyXCursor tmpcursor = cursor; /* store the current cursor */
534 // if there is no selection just set the layout
535 // of the current paragraph */
536 if (!selection.set()) {
537 selection.start = cursor; // dummy selection
538 selection.end = cursor;
540 Paragraph * endpar = setLayout(bview, cursor, selection.start,
541 selection.end, layout);
542 redoParagraphs(bview, selection.start, endpar);
544 // we have to reset the selection, because the
545 // geometry could have changed
546 setCursor(bview, selection.start.par(),
547 selection.start.pos(), false);
548 selection.cursor = cursor;
549 setCursor(bview, selection.end.par(), selection.end.pos(), false);
550 updateCounters(bview, cursor.row());
553 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
557 // increment depth over selection and
558 // make a total rebreak of those paragraphs
559 void LyXText::incDepth(BufferView * bview)
561 // If there is no selection, just use the current paragraph
562 if (!selection.set()) {
563 selection.start = cursor; // dummy selection
564 selection.end = cursor;
567 // We end at the next paragraph with depth 0
568 Paragraph * endpar = selection.end.par()->next();
570 Paragraph * undoendpar = endpar;
572 if (endpar && endpar->getDepth()) {
573 while (endpar && endpar->getDepth()) {
574 endpar = endpar->next();
578 endpar = endpar->next(); // because of parindents etc.
581 setUndo(bview, Undo::EDIT,
582 selection.start.par(), undoendpar);
584 LyXCursor tmpcursor = cursor; // store the current cursor
586 // ok we have a selection. This is always between sel_start_cursor
587 // and sel_end cursor
588 cursor = selection.start;
590 bool anything_changed = false;
593 // NOTE: you can't change the depth of a bibliography entry
594 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
595 Paragraph * prev = cursor.par()->previous();
598 if (cursor.par()->getDepth()
599 < prev->getMaxDepthAfter()) {
600 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
601 anything_changed = true;
605 if (cursor.par() == selection.end.par())
607 cursor.par(cursor.par()->next());
610 // if nothing changed set all depth to 0
611 if (!anything_changed) {
612 cursor = selection.start;
613 while (cursor.par() != selection.end.par()) {
614 cursor.par()->params().depth(0);
615 cursor.par(cursor.par()->next());
617 cursor.par()->params().depth(0);
620 redoParagraphs(bview, selection.start, endpar);
622 // we have to reset the selection, because the
623 // geometry could have changed
624 setCursor(bview, selection.start.par(), selection.start.pos());
625 selection.cursor = cursor;
626 setCursor(bview, selection.end.par(), selection.end.pos());
627 updateCounters(bview, cursor.row());
630 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
634 // decrement depth over selection and
635 // make a total rebreak of those paragraphs
636 void LyXText::decDepth(BufferView * bview)
638 // if there is no selection just set the layout
639 // of the current paragraph
640 if (!selection.set()) {
641 selection.start = cursor; // dummy selection
642 selection.end = cursor;
644 Paragraph * endpar = selection.end.par()->next();
645 Paragraph * undoendpar = endpar;
647 if (endpar && endpar->getDepth()) {
648 while (endpar && endpar->getDepth()) {
649 endpar = endpar->next();
653 endpar = endpar->next(); // because of parindents etc.
656 setUndo(bview, Undo::EDIT,
657 selection.start.par(), undoendpar);
659 LyXCursor tmpcursor = cursor; // store the current cursor
661 // ok we have a selection. This is always between sel_start_cursor
662 // and sel_end cursor
663 cursor = selection.start;
666 if (cursor.par()->params().depth()) {
667 cursor.par()->params()
668 .depth(cursor.par()->params().depth() - 1);
670 if (cursor.par() == selection.end.par()) {
673 cursor.par(cursor.par()->next());
676 redoParagraphs(bview, selection.start, endpar);
678 // we have to reset the selection, because the
679 // geometry could have changed
680 setCursor(bview, selection.start.par(),
681 selection.start.pos());
682 selection.cursor = cursor;
683 setCursor(bview, selection.end.par(), selection.end.pos());
684 updateCounters(bview, cursor.row());
687 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
691 // set font over selection and make a total rebreak of those paragraphs
692 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
694 // if there is no selection just set the current_font
695 if (!selection.set()) {
696 // Determine basis font
698 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
700 layoutfont = getLabelFont(bview->buffer(),
703 layoutfont = getLayoutFont(bview->buffer(),
706 // Update current font
707 real_current_font.update(font,
708 bview->buffer()->params.language,
711 // Reduce to implicit settings
712 current_font = real_current_font;
713 current_font.reduce(layoutfont);
714 // And resolve it completely
715 #ifndef INHERIT_LANGUAGE
716 real_current_font.realize(layoutfont);
718 real_current_font.realize(layoutfont,
719 bview->buffer()->params.language);
724 LyXCursor tmpcursor = cursor; // store the current cursor
726 // ok we have a selection. This is always between sel_start_cursor
727 // and sel_end cursor
729 setUndo(bview, Undo::EDIT,
730 selection.start.par(), selection.end.par()->next());
732 cursor = selection.start;
733 while (cursor.par() != selection.end.par() ||
734 cursor.pos() < selection.end.pos())
736 if (cursor.pos() < cursor.par()->size()) {
737 // an open footnote should behave like a closed one
738 setCharFont(bview, cursor.par(), cursor.pos(),
740 cursor.pos(cursor.pos() + 1);
743 cursor.par(cursor.par()->next());
748 redoParagraphs(bview, selection.start, selection.end.par()->next());
750 // we have to reset the selection, because the
751 // geometry could have changed, but we keep
752 // it for user convenience
753 setCursor(bview, selection.start.par(), selection.start.pos());
754 selection.cursor = cursor;
755 setCursor(bview, selection.end.par(), selection.end.pos());
757 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
758 tmpcursor.boundary());
762 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
764 Row * tmprow = cur.row();
765 int y = cur.y() - tmprow->baseline();
767 setHeightOfRow(bview, tmprow);
769 while (tmprow->previous()
770 && tmprow->previous()->par() == tmprow->par()) {
771 tmprow = tmprow->previous();
772 y -= tmprow->height();
773 setHeightOfRow(bview, tmprow);
776 // we can set the refreshing parameters now
777 status(bview, LyXText::NEED_MORE_REFRESH);
779 refresh_row = tmprow;
780 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
784 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
786 Row * tmprow = cur.row();
788 int y = cur.y() - tmprow->baseline();
789 setHeightOfRow(bview, tmprow);
791 while (tmprow->previous()
792 && tmprow->previous()->par() == tmprow->par()) {
793 tmprow = tmprow->previous();
794 y -= tmprow->height();
797 // we can set the refreshing parameters now
798 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
800 refresh_row = tmprow;
802 status(bview, LyXText::NEED_MORE_REFRESH);
803 setCursor(bview, cur.par(), cur.pos());
807 // deletes and inserts again all paragaphs between the cursor
808 // and the specified par
809 // This function is needed after SetLayout and SetFont etc.
810 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
811 Paragraph const * endpar) const
814 Paragraph * tmppar = 0;
815 Paragraph * first_phys_par = 0;
817 Row * tmprow = cur.row();
819 int y = cur.y() - tmprow->baseline();
821 if (!tmprow->previous()) {
822 // a trick/hack for UNDO
823 // This is needed because in an UNDO/REDO we could have changed
824 // the ownerParagrah() so the paragraph inside the row is NOT
825 // my really first par anymore. Got it Lars ;) (Jug 20011206)
826 first_phys_par = ownerParagraph();
828 first_phys_par = tmprow->par();
829 while (tmprow->previous()
830 && tmprow->previous()->par() == first_phys_par)
832 tmprow = tmprow->previous();
833 y -= tmprow->height();
837 // we can set the refreshing parameters now
838 status(bview, LyXText::NEED_MORE_REFRESH);
840 refresh_row = tmprow->previous(); /* the real refresh row will
841 be deleted, so I store
845 tmppar = tmprow->next()->par();
848 while (tmprow->next() && tmppar != endpar) {
849 removeRow(tmprow->next());
850 if (tmprow->next()) {
851 tmppar = tmprow->next()->par();
857 // remove the first one
858 tmprow2 = tmprow; /* this is because tmprow->previous()
860 tmprow = tmprow->previous();
863 tmppar = first_phys_par;
867 insertParagraph(bview, tmppar, tmprow);
871 while (tmprow->next()
872 && tmprow->next()->par() == tmppar) {
873 tmprow = tmprow->next();
875 tmppar = tmppar->next();
877 } while (tmppar && tmppar != endpar);
879 // this is because of layout changes
881 refresh_y -= refresh_row->height();
882 setHeightOfRow(bview, refresh_row);
884 refresh_row = firstrow;
886 setHeightOfRow(bview, refresh_row);
889 if (tmprow && tmprow->next())
890 setHeightOfRow(bview, tmprow->next());
894 void LyXText::fullRebreak(BufferView * bview)
900 if (need_break_row) {
901 breakAgain(bview, need_break_row);
908 // important for the screen
911 // the cursor set functions have a special mechanism. When they
912 // realize, that you left an empty paragraph, they will delete it.
913 // They also delete the corresponding row
915 // need the selection cursor:
916 void LyXText::setSelection(BufferView * bview)
918 bool const lsel = selection.set();
920 if (!selection.set()) {
921 last_sel_cursor = selection.cursor;
922 selection.start = selection.cursor;
923 selection.end = selection.cursor;
928 // first the toggling area
929 if (cursor.y() < last_sel_cursor.y()
930 || (cursor.y() == last_sel_cursor.y()
931 && cursor.x() < last_sel_cursor.x())) {
932 toggle_end_cursor = last_sel_cursor;
933 toggle_cursor = cursor;
935 toggle_end_cursor = cursor;
936 toggle_cursor = last_sel_cursor;
939 last_sel_cursor = cursor;
941 // and now the whole selection
943 if (selection.cursor.par() == cursor.par())
944 if (selection.cursor.pos() < cursor.pos()) {
945 selection.end = cursor;
946 selection.start = selection.cursor;
948 selection.end = selection.cursor;
949 selection.start = cursor;
951 else if (selection.cursor.y() < cursor.y() ||
952 (selection.cursor.y() == cursor.y()
953 && selection.cursor.x() < cursor.x())) {
954 selection.end = cursor;
955 selection.start = selection.cursor;
958 selection.end = selection.cursor;
959 selection.start = cursor;
962 // a selection with no contents is not a selection
963 if (selection.start.par() == selection.end.par() &&
964 selection.start.pos() == selection.end.pos())
965 selection.set(false);
967 if (inset_owner && (selection.set() || lsel))
968 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
972 string const LyXText::selectionAsString(Buffer const * buffer,
975 if (!selection.set()) return string();
978 // Special handling if the whole selection is within one paragraph
979 if (selection.start.par() == selection.end.par()) {
980 result += selection.start.par()->asString(buffer,
981 selection.start.pos(),
987 // The selection spans more than one paragraph
989 // First paragraph in selection
990 result += selection.start.par()->asString(buffer,
991 selection.start.pos(),
992 selection.start.par()->size(),
996 // The paragraphs in between (if any)
997 LyXCursor tmpcur(selection.start);
998 tmpcur.par(tmpcur.par()->next());
999 while (tmpcur.par() != selection.end.par()) {
1000 result += tmpcur.par()->asString(buffer, 0,
1001 tmpcur.par()->size(),
1003 tmpcur.par(tmpcur.par()->next());
1006 // Last paragraph in selection
1007 result += selection.end.par()->asString(buffer, 0,
1008 selection.end.pos(), label);
1014 void LyXText::clearSelection() const
1016 selection.set(false);
1017 selection.mark(false);
1018 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1019 // reset this in the bv_owner!
1020 if (bv_owner && bv_owner->text)
1021 bv_owner->text->xsel_cache.set(false);
1025 void LyXText::cursorHome(BufferView * bview) const
1027 setCursor(bview, cursor.par(), cursor.row()->pos());
1031 void LyXText::cursorEnd(BufferView * bview) const
1033 if (!cursor.row()->next()
1034 || cursor.row()->next()->par() != cursor.row()->par()) {
1035 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1037 if (cursor.par()->size() &&
1038 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1039 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1040 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1042 setCursor(bview,cursor.par(),
1043 rowLast(cursor.row()) + 1);
1049 void LyXText::cursorTop(BufferView * bview) const
1051 while (cursor.par()->previous())
1052 cursor.par(cursor.par()->previous());
1053 setCursor(bview, cursor.par(), 0);
1057 void LyXText::cursorBottom(BufferView * bview) const
1059 while (cursor.par()->next())
1060 cursor.par(cursor.par()->next());
1061 setCursor(bview, cursor.par(), cursor.par()->size());
1065 void LyXText::toggleFree(BufferView * bview,
1066 LyXFont const & font, bool toggleall)
1068 // If the mask is completely neutral, tell user
1069 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1070 // Could only happen with user style
1071 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1075 // Try implicit word selection
1076 // If there is a change in the language the implicit word selection
1078 LyXCursor resetCursor = cursor;
1079 bool implicitSelection = (font.language() == ignore_language
1080 && font.number() == LyXFont::IGNORE)
1081 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1084 setFont(bview, font, toggleall);
1086 // Implicit selections are cleared afterwards
1087 //and cursor is set to the original position.
1088 if (implicitSelection) {
1090 cursor = resetCursor;
1091 setCursor(bview, cursor.par(), cursor.pos());
1092 selection.cursor = cursor;
1095 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1099 string LyXText::getStringToIndex(BufferView * bview)
1103 // Try implicit word selection
1104 // If there is a change in the language the implicit word selection
1106 LyXCursor resetCursor = cursor;
1107 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1109 if (!selection.set()) {
1110 bview->owner()->message(_("Nothing to index!"));
1113 if (selection.start.par() != selection.end.par()) {
1114 bview->owner()->message(_("Cannot index more than one paragraph!"));
1118 idxstring = selectionAsString(bview->buffer(), false);
1120 // Implicit selections are cleared afterwards
1121 //and cursor is set to the original position.
1122 if (implicitSelection) {
1124 cursor = resetCursor;
1125 setCursor(bview, cursor.par(), cursor.pos());
1126 selection.cursor = cursor;
1132 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1133 Paragraph const * par) const
1135 if (par->layout()->labeltype != LABEL_MANUAL)
1138 return par->beginningOfMainBody();
1142 // the DTP switches for paragraphs. LyX will store them in the first
1143 // physicla paragraph. When a paragraph is broken, the top settings rest,
1144 // the bottom settings are given to the new one. So I can make shure,
1145 // they do not duplicate themself and you cannnot make dirty things with
1148 void LyXText::setParagraph(BufferView * bview,
1149 bool line_top, bool line_bottom,
1150 bool pagebreak_top, bool pagebreak_bottom,
1151 VSpace const & space_top,
1152 VSpace const & space_bottom,
1153 Spacing const & spacing,
1155 string labelwidthstring,
1158 LyXCursor tmpcursor = cursor;
1159 if (!selection.set()) {
1160 selection.start = cursor;
1161 selection.end = cursor;
1164 // make sure that the depth behind the selection are restored, too
1165 Paragraph * endpar = selection.end.par()->next();
1166 Paragraph * undoendpar = endpar;
1168 if (endpar && endpar->getDepth()) {
1169 while (endpar && endpar->getDepth()) {
1170 endpar = endpar->next();
1171 undoendpar = endpar;
1175 // because of parindents etc.
1176 endpar = endpar->next();
1179 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1182 Paragraph * tmppar = selection.end.par();
1184 while (tmppar != selection.start.par()->previous()) {
1185 setCursor(bview, tmppar, 0);
1186 status(bview, LyXText::NEED_MORE_REFRESH);
1187 refresh_row = cursor.row();
1188 refresh_y = cursor.y() - cursor.row()->baseline();
1189 cursor.par()->params().lineTop(line_top);
1190 cursor.par()->params().lineBottom(line_bottom);
1191 cursor.par()->params().pagebreakTop(pagebreak_top);
1192 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1193 cursor.par()->params().spaceTop(space_top);
1194 cursor.par()->params().spaceBottom(space_bottom);
1195 cursor.par()->params().spacing(spacing);
1196 // does the layout allow the new alignment?
1197 LyXLayout_ptr const & layout = cursor.par()->layout();
1199 if (align == LYX_ALIGN_LAYOUT)
1200 align = layout->align;
1201 if (align & layout->alignpossible) {
1202 if (align == layout->align)
1203 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1205 cursor.par()->params().align(align);
1207 cursor.par()->setLabelWidthString(labelwidthstring);
1208 cursor.par()->params().noindent(noindent);
1209 tmppar = cursor.par()->previous();
1212 redoParagraphs(bview, selection.start, endpar);
1215 setCursor(bview, selection.start.par(), selection.start.pos());
1216 selection.cursor = cursor;
1217 setCursor(bview, selection.end.par(), selection.end.pos());
1218 setSelection(bview);
1219 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1221 bview->updateInset(inset_owner, true);
1225 char loweralphaCounter(int n)
1227 if (n < 1 || n > 26)
1237 char alphaCounter(int n)
1239 if (n < 1 || n > 26)
1247 char hebrewCounter(int n)
1249 static const char hebrew[22] = {
1250 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1251 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1252 '÷', 'ø', 'ù', 'ú'
1254 if (n < 1 || n > 22)
1262 string const romanCounter(int n)
1264 static char const * roman[20] = {
1265 "i", "ii", "iii", "iv", "v",
1266 "vi", "vii", "viii", "ix", "x",
1267 "xi", "xii", "xiii", "xiv", "xv",
1268 "xvi", "xvii", "xviii", "xix", "xx"
1270 if (n < 1 || n > 20)
1279 // set the counter of a paragraph. This includes the labels
1280 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1282 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1283 LyXLayout_ptr const & layout = par->layout();
1285 // copy the prev-counters to this one,
1286 // unless this is the first paragraph
1287 if (par->previous()) {
1288 for (int i = 0; i < 10; ++i) {
1289 par->setCounter(i, par->previous()->getFirstCounter(i));
1291 par->params().appendix(par->previous()->params().appendix());
1292 if (!par->params().appendix() && par->params().startOfAppendix()) {
1293 par->params().appendix(true);
1294 for (int i = 0; i < 10; ++i) {
1295 par->setCounter(i, 0);
1298 par->enumdepth = par->previous()->enumdepth;
1299 par->itemdepth = par->previous()->itemdepth;
1301 for (int i = 0; i < 10; ++i) {
1302 par->setCounter(i, 0);
1304 par->params().appendix(par->params().startOfAppendix());
1309 /* Maybe we have to increment the enumeration depth.
1310 * BUT, enumeration in a footnote is considered in isolation from its
1311 * surrounding paragraph so don't increment if this is the
1312 * first line of the footnote
1313 * AND, bibliographies can't have their depth changed ie. they
1314 * are always of depth 0
1317 && par->previous()->getDepth() < par->getDepth()
1318 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1319 && par->enumdepth < 3
1320 && layout->labeltype != LABEL_BIBLIO) {
1324 // Maybe we have to decrement the enumeration depth, see note above
1326 && par->previous()->getDepth() > par->getDepth()
1327 && layout->labeltype != LABEL_BIBLIO) {
1328 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1329 par->setCounter(6 + par->enumdepth,
1330 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1331 // reset the counters.A depth change is like a breaking layout
1332 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1333 par->setCounter(i, 0);
1336 if (!par->params().labelString().empty()) {
1337 par->params().labelString(string());
1340 if (layout->margintype == MARGIN_MANUAL) {
1341 if (par->params().labelWidthString().empty()) {
1342 par->setLabelWidthString(layout->labelstring());
1345 par->setLabelWidthString(string());
1348 // is it a layout that has an automatic label?
1349 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1351 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1352 if (i >= 0 && i<= buf->params.secnumdepth) {
1353 par->incCounter(i); // increment the counter
1355 // Is there a label? Useful for Chapter layout
1356 if (!par->params().appendix()) {
1357 if (!layout->labelstring().empty())
1358 par->params().labelString(layout->labelstring());
1360 par->params().labelString(string());
1362 if (!layout->labelstring_appendix().empty())
1363 par->params().labelString(layout->labelstring_appendix());
1365 par->params().labelString(string());
1370 if (!par->params().appendix()) {
1371 switch (2 * LABEL_COUNTER_CHAPTER -
1372 textclass.maxcounter() + i) {
1373 case LABEL_COUNTER_CHAPTER:
1374 s << par->getCounter(i);
1376 case LABEL_COUNTER_SECTION:
1377 s << par->getCounter(i - 1) << '.'
1378 << par->getCounter(i);
1380 case LABEL_COUNTER_SUBSECTION:
1381 s << par->getCounter(i - 2) << '.'
1382 << par->getCounter(i - 1) << '.'
1383 << par->getCounter(i);
1385 case LABEL_COUNTER_SUBSUBSECTION:
1386 s << par->getCounter(i - 3) << '.'
1387 << par->getCounter(i - 2) << '.'
1388 << par->getCounter(i - 1) << '.'
1389 << par->getCounter(i);
1392 case LABEL_COUNTER_PARAGRAPH:
1393 s << par->getCounter(i - 4) << '.'
1394 << par->getCounter(i - 3) << '.'
1395 << par->getCounter(i - 2) << '.'
1396 << par->getCounter(i - 1) << '.'
1397 << par->getCounter(i);
1399 case LABEL_COUNTER_SUBPARAGRAPH:
1400 s << par->getCounter(i - 5) << '.'
1401 << par->getCounter(i - 4) << '.'
1402 << par->getCounter(i - 3) << '.'
1403 << par->getCounter(i - 2) << '.'
1404 << par->getCounter(i - 1) << '.'
1405 << par->getCounter(i);
1409 // Can this ever be reached? And in the
1410 // case it is, how can this be correct?
1412 s << par->getCounter(i) << '.';
1415 } else { // appendix
1416 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1417 case LABEL_COUNTER_CHAPTER:
1418 if (par->isRightToLeftPar(buf->params))
1419 s << hebrewCounter(par->getCounter(i));
1421 s << alphaCounter(par->getCounter(i));
1423 case LABEL_COUNTER_SECTION:
1424 if (par->isRightToLeftPar(buf->params))
1425 s << hebrewCounter(par->getCounter(i - 1));
1427 s << alphaCounter(par->getCounter(i - 1));
1430 << par->getCounter(i);
1433 case LABEL_COUNTER_SUBSECTION:
1434 if (par->isRightToLeftPar(buf->params))
1435 s << hebrewCounter(par->getCounter(i - 2));
1437 s << alphaCounter(par->getCounter(i - 2));
1440 << par->getCounter(i-1) << '.'
1441 << par->getCounter(i);
1444 case LABEL_COUNTER_SUBSUBSECTION:
1445 if (par->isRightToLeftPar(buf->params))
1446 s << hebrewCounter(par->getCounter(i-3));
1448 s << alphaCounter(par->getCounter(i-3));
1451 << par->getCounter(i-2) << '.'
1452 << par->getCounter(i-1) << '.'
1453 << par->getCounter(i);
1456 case LABEL_COUNTER_PARAGRAPH:
1457 if (par->isRightToLeftPar(buf->params))
1458 s << hebrewCounter(par->getCounter(i-4));
1460 s << alphaCounter(par->getCounter(i-4));
1463 << par->getCounter(i-3) << '.'
1464 << par->getCounter(i-2) << '.'
1465 << par->getCounter(i-1) << '.'
1466 << par->getCounter(i);
1469 case LABEL_COUNTER_SUBPARAGRAPH:
1470 if (par->isRightToLeftPar(buf->params))
1471 s << hebrewCounter(par->getCounter(i-5));
1473 s << alphaCounter(par->getCounter(i-5));
1476 << par->getCounter(i-4) << '.'
1477 << par->getCounter(i-3) << '.'
1478 << par->getCounter(i-2) << '.'
1479 << par->getCounter(i-1) << '.'
1480 << par->getCounter(i);
1484 // Can this ever be reached? And in the
1485 // case it is, how can this be correct?
1487 s << par->getCounter(i) << '.';
1493 par->params().labelString(par->params().labelString() +s.str().c_str());
1494 // We really want to remove the c_str as soon as
1497 for (i++; i < 10; ++i) {
1498 // reset the following counters
1499 par->setCounter(i, 0);
1501 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1502 for (i++; i < 10; ++i) {
1503 // reset the following counters
1504 par->setCounter(i, 0);
1506 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1507 par->incCounter(i + par->enumdepth);
1508 int number = par->getCounter(i + par->enumdepth);
1512 switch (par->enumdepth) {
1514 if (par->isRightToLeftPar(buf->params))
1516 << hebrewCounter(number)
1520 << loweralphaCounter(number)
1524 if (par->isRightToLeftPar(buf->params))
1525 s << '.' << romanCounter(number);
1527 s << romanCounter(number) << '.';
1530 if (par->isRightToLeftPar(buf->params))
1532 << alphaCounter(number);
1534 s << alphaCounter(number)
1538 if (par->isRightToLeftPar(buf->params))
1545 par->params().labelString(s.str().c_str());
1547 for (i += par->enumdepth + 1; i < 10; ++i) {
1548 // reset the following counters
1549 par->setCounter(i, 0);
1553 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1554 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1556 int number = par->getCounter(i);
1558 InsetCommandParams p("bibitem" );
1559 par->bibkey = new InsetBibKey(p);
1561 par->bibkey->setCounter(number);
1562 par->params().labelString(layout->labelstring());
1564 // In biblio should't be following counters but...
1566 string s = layout->labelstring();
1568 // the caption hack:
1569 if (layout->labeltype == LABEL_SENSITIVE) {
1570 bool isOK (par->inInset() && par->inInset()->owner() &&
1571 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1574 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1576 = floatList.getType(tmp->type());
1577 // We should get the correct number here too.
1578 s = fl.name() + " #:";
1580 /* par->SetLayout(0);
1581 s = layout->labelstring; */
1582 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1583 ? " :úåòîùî øñç" : "Senseless: ";
1586 par->params().labelString(s);
1588 /* reset the enumeration counter. They are always resetted
1589 * when there is any other layout between */
1590 for (int i = 6 + par->enumdepth; i < 10; ++i)
1591 par->setCounter(i, 0);
1596 // Updates all counters BEHIND the row. Changed paragraphs
1597 // with a dynamic left margin will be rebroken.
1598 void LyXText::updateCounters(BufferView * bview, Row * row) const
1606 par = row->par()->next();
1610 while (row->par() != par)
1613 setCounter(bview->buffer(), par);
1615 // now check for the headline layouts. remember that they
1616 // have a dynamic left margin
1617 LyXLayout_ptr const & layout = par->layout();
1619 if (layout->margintype == MARGIN_DYNAMIC
1620 || layout->labeltype == LABEL_SENSITIVE) {
1621 // Rebreak the paragraph
1622 removeParagraph(row);
1623 appendParagraph(bview, row);
1630 void LyXText::insertInset(BufferView * bview, Inset * inset)
1632 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1634 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1636 cursor.par()->insertInset(cursor.pos(), inset);
1637 // Just to rebreak and refresh correctly.
1638 // The character will not be inserted a second time
1639 insertChar(bview, Paragraph::META_INSET);
1640 // If we enter a highly editable inset the cursor should be to before
1641 // the inset. This couldn't happen before as Undo was not handled inside
1642 // inset now after the Undo LyX tries to call inset->Edit(...) again
1643 // and cannot do this as the cursor is behind the inset and GetInset
1644 // does not return the inset!
1645 if (isHighlyEditableInset(inset)) {
1646 cursorLeft(bview, true);
1652 void LyXText::copyEnvironmentType()
1654 copylayouttype = cursor.par()->layout()->name();
1658 void LyXText::pasteEnvironmentType(BufferView * bview)
1660 setLayout(bview, copylayouttype);
1664 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1666 // Stuff what we got on the clipboard. Even if there is no selection.
1668 // There is a problem with having the stuffing here in that the
1669 // larger the selection the slower LyX will get. This can be
1670 // solved by running the line below only when the selection has
1671 // finished. The solution used currently just works, to make it
1672 // faster we need to be more clever and probably also have more
1673 // calls to stuffClipboard. (Lgb)
1674 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1676 // This doesn't make sense, if there is no selection
1677 if (!selection.set())
1680 // OK, we have a selection. This is always between selection.start
1681 // and selection.end
1683 // make sure that the depth behind the selection are restored, too
1684 Paragraph * endpar = selection.end.par()->next();
1685 Paragraph * undoendpar = endpar;
1687 if (endpar && endpar->getDepth()) {
1688 while (endpar && endpar->getDepth()) {
1689 endpar = endpar->next();
1690 undoendpar = endpar;
1692 } else if (endpar) {
1693 endpar = endpar->next(); // because of parindents etc.
1696 setUndo(bview, Undo::DELETE,
1697 selection.start.par(), undoendpar);
1699 // there are two cases: cut only within one paragraph or
1700 // more than one paragraph
1701 if (selection.start.par() == selection.end.par()) {
1702 // only within one paragraph
1703 endpar = selection.end.par();
1704 int pos = selection.end.pos();
1705 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1706 selection.start.pos(), pos,
1707 bview->buffer()->params.textclass,
1709 selection.end.pos(pos);
1711 endpar = selection.end.par();
1712 int pos = selection.end.pos();
1713 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1714 selection.start.pos(), pos,
1715 bview->buffer()->params.textclass,
1718 selection.end.par(endpar);
1719 selection.end.pos(pos);
1720 cursor.pos(selection.end.pos());
1722 endpar = endpar->next();
1724 // sometimes necessary
1726 selection.start.par()->stripLeadingSpaces();
1728 redoParagraphs(bview, selection.start, endpar);
1730 // cutSelection can invalidate the cursor so we need to set
1732 cursor = selection.start;
1734 // need a valid cursor. (Lgb)
1737 setCursor(bview, cursor.par(), cursor.pos());
1738 selection.cursor = cursor;
1739 updateCounters(bview, cursor.row());
1743 void LyXText::copySelection(BufferView * bview)
1745 // stuff the selection onto the X clipboard, from an explicit copy request
1746 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1748 // this doesnt make sense, if there is no selection
1749 if (!selection.set())
1752 // ok we have a selection. This is always between selection.start
1753 // and sel_end cursor
1755 // copy behind a space if there is one
1756 while (selection.start.par()->size() > selection.start.pos()
1757 && selection.start.par()->isLineSeparator(selection.start.pos())
1758 && (selection.start.par() != selection.end.par()
1759 || selection.start.pos() < selection.end.pos()))
1760 selection.start.pos(selection.start.pos() + 1);
1762 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1763 selection.start.pos(), selection.end.pos(),
1764 bview->buffer()->params.textclass);
1768 void LyXText::pasteSelection(BufferView * bview)
1770 // this does not make sense, if there is nothing to paste
1771 if (!CutAndPaste::checkPastePossible(cursor.par()))
1774 setUndo(bview, Undo::INSERT,
1775 cursor.par(), cursor.par()->next());
1778 Paragraph * actpar = cursor.par();
1779 int pos = cursor.pos();
1781 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1782 bview->buffer()->params.textclass);
1784 redoParagraphs(bview, cursor, endpar);
1786 setCursor(bview, cursor.par(), cursor.pos());
1789 selection.cursor = cursor;
1790 setCursor(bview, actpar, pos);
1791 setSelection(bview);
1792 updateCounters(bview, cursor.row());
1796 // sets the selection over the number of characters of string, no check!!
1797 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1802 selection.cursor = cursor;
1803 for (string::size_type i = 0; i < str.length(); ++i)
1805 setSelection(bview);
1809 // simple replacing. The font of the first selected character is used
1810 void LyXText::replaceSelectionWithString(BufferView * bview,
1813 setCursorParUndo(bview);
1816 if (!selection.set()) { // create a dummy selection
1817 selection.end = cursor;
1818 selection.start = cursor;
1821 // Get font setting before we cut
1822 pos_type pos = selection.end.pos();
1823 LyXFont const font = selection.start.par()
1824 ->getFontSettings(bview->buffer()->params,
1825 selection.start.pos());
1827 // Insert the new string
1828 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1829 selection.end.par()->insertChar(pos, (*cit), font);
1833 // Cut the selection
1834 cutSelection(bview, true, false);
1840 // needed to insert the selection
1841 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1843 Paragraph * par = cursor.par();
1844 pos_type pos = cursor.pos();
1845 Paragraph * endpar = cursor.par()->next();
1847 setCursorParUndo(bview);
1849 // only to be sure, should not be neccessary
1852 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1854 redoParagraphs(bview, cursor, endpar);
1855 setCursor(bview, cursor.par(), cursor.pos());
1856 selection.cursor = cursor;
1857 setCursor(bview, par, pos);
1858 setSelection(bview);
1862 // turns double-CR to single CR, others where converted into one
1863 // blank. Then InsertStringAsLines is called
1864 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1866 string linestr(str);
1867 bool newline_inserted = false;
1868 for (string::size_type i = 0; i < linestr.length(); ++i) {
1869 if (linestr[i] == '\n') {
1870 if (newline_inserted) {
1871 // we know that \r will be ignored by
1872 // InsertStringA. Of course, it is a dirty
1873 // trick, but it works...
1874 linestr[i - 1] = '\r';
1878 newline_inserted = true;
1880 } else if (IsPrintable(linestr[i])) {
1881 newline_inserted = false;
1884 insertStringAsLines(bview, linestr);
1888 bool LyXText::gotoNextInset(BufferView * bview,
1889 vector<Inset::Code> const & codes,
1890 string const & contents) const
1892 LyXCursor res = cursor;
1895 if (res.pos() < res.par()->size() - 1) {
1896 res.pos(res.pos() + 1);
1898 res.par(res.par()->next());
1902 } while (res.par() &&
1903 !(res.par()->isInset(res.pos())
1904 && (inset = res.par()->getInset(res.pos())) != 0
1905 && find(codes.begin(), codes.end(), inset->lyxCode())
1907 && (contents.empty() ||
1908 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1912 setCursor(bview, res.par(), res.pos(), false);
1919 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1922 LyXCursor tmpcursor;
1926 Row * row = getRow(par, pos, y);
1928 // is there a break one row above
1929 if (row->previous() && row->previous()->par() == row->par()) {
1930 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1931 if (z >= row->pos()) {
1932 // set the dimensions of the row above
1933 y -= row->previous()->height();
1935 refresh_row = row->previous();
1936 status(bview, LyXText::NEED_MORE_REFRESH);
1938 breakAgain(bview, row->previous());
1940 // set the cursor again. Otherwise
1941 // dangling pointers are possible
1942 setCursor(bview, cursor.par(), cursor.pos(),
1943 false, cursor.boundary());
1944 selection.cursor = cursor;
1949 int const tmpheight = row->height();
1950 pos_type const tmplast = rowLast(row);
1954 breakAgain(bview, row);
1955 if (row->height() == tmpheight && rowLast(row) == tmplast)
1956 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1958 status(bview, LyXText::NEED_MORE_REFRESH);
1960 // check the special right address boxes
1961 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1968 redoDrawingOfParagraph(bview, tmpcursor);
1971 // set the cursor again. Otherwise dangling pointers are possible
1972 // also set the selection
1974 if (selection.set()) {
1976 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1977 false, selection.cursor.boundary());
1978 selection.cursor = cursor;
1979 setCursorIntern(bview, selection.start.par(),
1980 selection.start.pos(),
1981 false, selection.start.boundary());
1982 selection.start = cursor;
1983 setCursorIntern(bview, selection.end.par(),
1984 selection.end.pos(),
1985 false, selection.end.boundary());
1986 selection.end = cursor;
1987 setCursorIntern(bview, last_sel_cursor.par(),
1988 last_sel_cursor.pos(),
1989 false, last_sel_cursor.boundary());
1990 last_sel_cursor = cursor;
1993 setCursorIntern(bview, cursor.par(), cursor.pos(),
1994 false, cursor.boundary());
1998 // returns false if inset wasn't found
1999 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2001 // first check the current paragraph
2002 int pos = cursor.par()->getPositionOfInset(inset);
2004 checkParagraph(bview, cursor.par(), pos);
2008 // check every paragraph
2010 Paragraph * par = ownerParagraph();
2012 pos = par->getPositionOfInset(inset);
2014 checkParagraph(bview, par, pos);
2024 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2026 bool setfont, bool boundary) const
2028 LyXCursor old_cursor = cursor;
2029 setCursorIntern(bview, par, pos, setfont, boundary);
2030 return deleteEmptyParagraphMechanism(bview, old_cursor);
2034 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2035 pos_type pos, bool boundary) const
2042 cur.boundary(boundary);
2044 // get the cursor y position in text
2046 Row * row = getRow(par, pos, y);
2047 Row * old_row = row;
2049 // if we are before the first char of this row and are still in the
2050 // same paragraph and there is a previous row then put the cursor on
2051 // the end of the previous row
2052 cur.iy(y + row->baseline());
2054 if (row->previous() && pos &&
2055 row->previous()->par() == row->par() &&
2056 par->getChar(pos) == Paragraph::META_INSET &&
2057 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2059 row = row->previous();
2064 // y is now the beginning of the cursor row
2065 y += row->baseline();
2066 // y is now the cursor baseline
2069 pos_type last = rowLastPrintable(old_row);
2071 if (pos > last + 1) {
2072 // This shouldn't happen.
2075 } else if (pos < row->pos()) {
2080 // now get the cursors x position
2081 float x = getCursorX(bview, row, pos, last, boundary);
2084 if (old_row != row) {
2085 x = getCursorX(bview, old_row, pos, last, boundary);
2092 float LyXText::getCursorX(BufferView * bview, Row * row,
2093 pos_type pos, pos_type last, bool boundary) const
2095 pos_type cursor_vpos = 0;
2097 float fill_separator;
2099 float fill_label_hfill;
2100 // This call HAS to be here because of the BidiTables!!!
2101 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2104 if (last < row->pos())
2105 cursor_vpos = row->pos();
2106 else if (pos > last && !boundary)
2107 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2108 ? row->pos() : last + 1;
2109 else if (pos > row->pos() &&
2110 (pos > last || boundary))
2111 /// Place cursor after char at (logical) position pos - 1
2112 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2113 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2115 /// Place cursor before char at (logical) position pos
2116 cursor_vpos = (bidi_level(pos) % 2 == 0)
2117 ? log2vis(pos) : log2vis(pos) + 1;
2119 pos_type main_body =
2120 beginningOfMainBody(bview->buffer(), row->par());
2121 if ((main_body > 0) &&
2122 ((main_body-1 > last) ||
2123 !row->par()->isLineSeparator(main_body-1)))
2126 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2127 pos_type pos = vis2log(vpos);
2128 if (main_body > 0 && pos == main_body - 1) {
2129 x += fill_label_hfill +
2130 font_metrics::width(
2131 row->par()->layout()->labelsep,
2132 getLabelFont(bview->buffer(),
2134 if (row->par()->isLineSeparator(main_body - 1))
2135 x -= singleWidth(bview,
2136 row->par(), main_body - 1);
2138 if (hfillExpansion(bview->buffer(), row, pos)) {
2139 x += singleWidth(bview, row->par(), pos);
2140 if (pos >= main_body)
2143 x += fill_label_hfill;
2144 } else if (row->par()->isSeparator(pos)) {
2145 x += singleWidth(bview, row->par(), pos);
2146 if (pos >= main_body)
2147 x += fill_separator;
2149 x += singleWidth(bview, row->par(), pos);
2155 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2156 pos_type pos, bool setfont, bool boundary) const
2158 InsetText * it = static_cast<InsetText *>(par->inInset());
2160 if (it != inset_owner) {
2161 lyxerr[Debug::INSETS] << "InsetText is " << it
2163 << "inset_owner is "
2164 << inset_owner << endl;
2165 #ifdef WITH_WARNINGS
2166 #warning I believe this code is wrong. (Lgb)
2167 #warning Jürgen, have a look at this. (Lgb)
2168 #warning Hmmm, I guess you are right but we
2169 #warning should verify when this is needed
2171 // Jürgen, would you like to have a look?
2172 // I guess we need to move the outer cursor
2173 // and open and lock the inset (bla bla bla)
2174 // stuff I don't know... so can you have a look?
2176 // I moved the lyxerr stuff in here so we can see if
2177 // this is actually really needed and where!
2179 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2184 setCursor(bview, cursor, par, pos, boundary);
2186 setCurrentFont(bview);
2190 void LyXText::setCurrentFont(BufferView * bview) const
2192 pos_type pos = cursor.pos();
2193 if (cursor.boundary() && pos > 0)
2197 if (pos == cursor.par()->size())
2199 else // potentional bug... BUG (Lgb)
2200 if (cursor.par()->isSeparator(pos)) {
2201 if (pos > cursor.row()->pos() &&
2202 bidi_level(pos) % 2 ==
2203 bidi_level(pos - 1) % 2)
2205 else if (pos + 1 < cursor.par()->size())
2211 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2212 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2214 if (cursor.pos() == cursor.par()->size() &&
2215 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2216 !cursor.boundary()) {
2217 Language const * lang =
2218 cursor.par()->getParLanguage(bview->buffer()->params);
2219 current_font.setLanguage(lang);
2220 current_font.setNumber(LyXFont::OFF);
2221 real_current_font.setLanguage(lang);
2222 real_current_font.setNumber(LyXFont::OFF);
2227 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2229 LyXCursor old_cursor = cursor;
2231 setCursorFromCoordinates(bview, cursor, x, y);
2232 setCurrentFont(bview);
2233 deleteEmptyParagraphMechanism(bview, old_cursor);
2237 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2240 // Get the row first.
2242 Row * row = getRowNearY(y);
2244 pos_type const column = getColumnNearX(bview, row, x, bound);
2245 cur.par(row->par());
2246 cur.pos(row->pos() + column);
2248 cur.y(y + row->baseline());
2251 if (row->next() && row->next()->pos() == cur.pos() &&
2252 cur.par() == row->next()->par() &&
2253 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2254 (ins=cur.par()->getInset(cur.pos())) &&
2255 (ins->needFullRow() || ins->display()))
2257 // we enter here if we put the cursor on the end of the row before
2258 // a inset which uses a full row and in that case we HAVE to calculate
2259 // the right (i) values.
2260 pos_type last = rowLastPrintable(row);
2261 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2263 cur.iy(y + row->height() + row->next()->baseline());
2264 cur.irow(row->next());
2270 cur.boundary(bound);
2274 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2276 if (cursor.pos() > 0) {
2277 bool boundary = cursor.boundary();
2278 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2279 if (!internal && !boundary &&
2280 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2281 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2282 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2283 Paragraph * par = cursor.par()->previous();
2284 setCursor(bview, par, par->size());
2289 void LyXText::cursorRight(BufferView * bview, bool internal) const
2291 if (!internal && cursor.boundary() &&
2292 !cursor.par()->isNewline(cursor.pos()))
2293 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2294 else if (cursor.pos() < cursor.par()->size()) {
2295 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2297 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2298 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2299 } else if (cursor.par()->next())
2300 setCursor(bview, cursor.par()->next(), 0);
2304 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2307 int x = cursor.x_fix();
2308 int y = cursor.y() - cursor.row()->baseline() - 1;
2309 setCursorFromCoordinates(bview, x, y);
2311 int y1 = cursor.iy() - first_y;
2315 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2316 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2317 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2321 setCursorFromCoordinates(bview, cursor.x_fix(),
2322 cursor.y() - cursor.row()->baseline() - 1);
2327 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2330 int x = cursor.x_fix();
2331 int y = cursor.y() - cursor.row()->baseline() +
2332 cursor.row()->height() + 1;
2333 setCursorFromCoordinates(bview, x, y);
2334 if (!selecting && cursor.row() == cursor.irow()) {
2335 int y1 = cursor.iy() - first_y;
2339 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2340 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2341 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2345 setCursorFromCoordinates(bview, cursor.x_fix(),
2346 cursor.y() - cursor.row()->baseline()
2347 + cursor.row()->height() + 1);
2352 void LyXText::cursorUpParagraph(BufferView * bview) const
2354 if (cursor.pos() > 0) {
2355 setCursor(bview, cursor.par(), 0);
2357 else if (cursor.par()->previous()) {
2358 setCursor(bview, cursor.par()->previous(), 0);
2363 void LyXText::cursorDownParagraph(BufferView * bview) const
2365 if (cursor.par()->next()) {
2366 setCursor(bview, cursor.par()->next(), 0);
2368 setCursor(bview, cursor.par(), cursor.par()->size());
2372 // fix the cursor `cur' after a characters has been deleted at `where'
2373 // position. Called by deleteEmptyParagraphMechanism
2374 void LyXText::fixCursorAfterDelete(BufferView * bview,
2376 LyXCursor const & where) const
2378 // if cursor is not in the paragraph where the delete occured,
2380 if (cur.par() != where.par())
2383 // if cursor position is after the place where the delete occured,
2385 if (cur.pos() > where.pos())
2386 cur.pos(cur.pos()-1);
2388 // check also if we don't want to set the cursor on a spot behind the
2389 // pagragraph because we erased the last character.
2390 if (cur.pos() > cur.par()->size())
2391 cur.pos(cur.par()->size());
2393 // recompute row et al. for this cursor
2394 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2398 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2399 LyXCursor const & old_cursor) const
2401 // Would be wrong to delete anything if we have a selection.
2402 if (selection.set())
2405 // We allow all kinds of "mumbo-jumbo" when freespacing.
2406 if (old_cursor.par()->layout()->free_spacing
2407 || old_cursor.par()->isFreeSpacing()) {
2411 /* Ok I'll put some comments here about what is missing.
2412 I have fixed BackSpace (and thus Delete) to not delete
2413 double-spaces automagically. I have also changed Cut,
2414 Copy and Paste to hopefully do some sensible things.
2415 There are still some small problems that can lead to
2416 double spaces stored in the document file or space at
2417 the beginning of paragraphs. This happens if you have
2418 the cursor betwenn to spaces and then save. Or if you
2419 cut and paste and the selection have a space at the
2420 beginning and then save right after the paste. I am
2421 sure none of these are very hard to fix, but I will
2422 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2423 that I can get some feedback. (Lgb)
2426 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2427 // delete the LineSeparator.
2430 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2431 // delete the LineSeparator.
2434 // If the pos around the old_cursor were spaces, delete one of them.
2435 if (old_cursor.par() != cursor.par()
2436 || old_cursor.pos() != cursor.pos()) {
2437 // Only if the cursor has really moved
2439 if (old_cursor.pos() > 0
2440 && old_cursor.pos() < old_cursor.par()->size()
2441 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2442 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2443 old_cursor.par()->erase(old_cursor.pos() - 1);
2444 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2446 #ifdef WITH_WARNINGS
2447 #warning This will not work anymore when we have multiple views of the same buffer
2448 // In this case, we will have to correct also the cursors held by
2449 // other bufferviews. It will probably be easier to do that in a more
2450 // automated way in LyXCursor code. (JMarc 26/09/2001)
2452 // correct all cursors held by the LyXText
2453 fixCursorAfterDelete(bview, cursor, old_cursor);
2454 fixCursorAfterDelete(bview, selection.cursor,
2456 fixCursorAfterDelete(bview, selection.start,
2458 fixCursorAfterDelete(bview, selection.end, old_cursor);
2459 fixCursorAfterDelete(bview, last_sel_cursor,
2461 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2462 fixCursorAfterDelete(bview, toggle_end_cursor,
2468 // don't delete anything if this is the ONLY paragraph!
2469 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2472 // Do not delete empty paragraphs with keepempty set.
2473 if (old_cursor.par()->layout()->keepempty)
2476 // only do our magic if we changed paragraph
2477 if (old_cursor.par() == cursor.par())
2480 // record if we have deleted a paragraph
2481 // we can't possibly have deleted a paragraph before this point
2482 bool deleted = false;
2484 if ((old_cursor.par()->size() == 0
2485 || (old_cursor.par()->size() == 1
2486 && old_cursor.par()->isLineSeparator(0)))) {
2487 // ok, we will delete anything
2488 LyXCursor tmpcursor;
2490 // make sure that you do not delete any environments
2491 status(bview, LyXText::NEED_MORE_REFRESH);
2494 if (old_cursor.row()->previous()) {
2495 refresh_row = old_cursor.row()->previous();
2496 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2498 cursor = old_cursor; // that undo can restore the right cursor position
2499 Paragraph * endpar = old_cursor.par()->next();
2500 if (endpar && endpar->getDepth()) {
2501 while (endpar && endpar->getDepth()) {
2502 endpar = endpar->next();
2505 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2509 removeRow(old_cursor.row());
2510 if (ownerParagraph() == old_cursor.par()) {
2511 ownerParagraph(ownerParagraph()->next());
2514 delete old_cursor.par();
2516 /* Breakagain the next par. Needed because of
2517 * the parindent that can occur or dissappear.
2518 * The next row can change its height, if
2519 * there is another layout before */
2520 if (refresh_row->next()) {
2521 breakAgain(bview, refresh_row->next());
2522 updateCounters(bview, refresh_row);
2524 setHeightOfRow(bview, refresh_row);
2526 refresh_row = old_cursor.row()->next();
2527 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2530 cursor = old_cursor; // that undo can restore the right cursor position
2531 Paragraph * endpar = old_cursor.par()->next();
2532 if (endpar && endpar->getDepth()) {
2533 while (endpar && endpar->getDepth()) {
2534 endpar = endpar->next();
2537 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2541 removeRow(old_cursor.row());
2543 if (ownerParagraph() == old_cursor.par()) {
2544 ownerParagraph(ownerParagraph()->next());
2547 delete old_cursor.par();
2549 /* Breakagain the next par. Needed because of
2550 the parindent that can occur or dissappear.
2551 The next row can change its height, if
2552 there is another layout before */
2554 breakAgain(bview, refresh_row);
2555 updateCounters(bview, refresh_row->previous());
2560 setCursorIntern(bview, cursor.par(), cursor.pos());
2562 if (selection.cursor.par() == old_cursor.par()
2563 && selection.cursor.pos() == old_cursor.pos()) {
2564 // correct selection
2565 selection.cursor = cursor;
2569 if (old_cursor.par()->stripLeadingSpaces()) {
2570 redoParagraphs(bview, old_cursor,
2571 old_cursor.par()->next());
2573 setCursorIntern(bview, cursor.par(), cursor.pos());
2574 selection.cursor = cursor;
2581 void LyXText::toggleAppendix(BufferView * bview)
2583 Paragraph * par = cursor.par();
2584 bool start = !par->params().startOfAppendix();
2586 // ensure that we have only one start_of_appendix in this document
2587 Paragraph * tmp = ownerParagraph();
2588 for (; tmp; tmp = tmp->next()) {
2589 tmp->params().startOfAppendix(false);
2592 par->params().startOfAppendix(start);
2594 // we can set the refreshing parameters now
2595 status(bview, LyXText::NEED_MORE_REFRESH);
2597 refresh_row = 0; // not needed for full update
2598 updateCounters(bview, 0);
2599 setCursor(bview, cursor.par(), cursor.pos());
2603 Paragraph * LyXText::ownerParagraph() const
2606 return inset_owner->paragraph();
2608 return bv_owner->buffer()->paragraph;
2612 void LyXText::ownerParagraph(Paragraph * p) const
2615 inset_owner->paragraph(p);
2617 bv_owner->buffer()->paragraph = p;
2622 void LyXText::ownerParagraph(int id, Paragraph * p) const
2624 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2625 if (op && op->inInset()) {
2626 static_cast<InsetText *>(op->inInset())->paragraph(p);
2633 LyXText::text_status LyXText::status() const
2639 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2641 // We should only go up with refreshing code so this means that if
2642 // we have a MORE refresh we should never set it to LITTLE if we still
2643 // didn't handle it (and then it will be UNCHANGED. Now as long as
2644 // we stay inside one LyXText this may work but we need to tell the
2645 // outermost LyXText that it should REALLY draw us if there is some
2646 // change in a Inset::LyXText. So you see that when we are inside a
2647 // inset's LyXText we give the LITTLE to the outermost LyXText to
2648 // tell'em that it should redraw the actual row (where the inset
2649 // resides! Capito?!
2651 if ((status_ != NEED_MORE_REFRESH)
2652 || (status_ == NEED_MORE_REFRESH
2653 && st != NEED_VERY_LITTLE_REFRESH))
2656 if (inset_owner && st != UNCHANGED) {
2657 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2658 if (!bview->text->refresh_row) {
2659 bview->text->refresh_row = bview->text->cursor.row();
2660 bview->text->refresh_y = bview->text->cursor.y() -
2661 bview->text->cursor.row()->baseline();