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"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
28 #include "CutAndPaste.h"
34 #include "FloatList.h"
36 #include "ParagraphParameters.h"
38 #include "insets/inseterror.h"
39 #include "insets/insetbib.h"
40 #include "insets/insetspecialchar.h"
41 #include "insets/insettext.h"
42 #include "insets/insetfloat.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
65 LyXText::LyXText(InsetText * inset)
66 : number_of_rows(0), height(0), width(0), first(0),
67 bv_owner(0), inset_owner(inset), the_locking_inset(0),
68 need_break_row(0), refresh_y(0), refresh_row(0),
69 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
73 void LyXText::init(BufferView * bview, bool reinit)
76 // Delete all rows, this does not touch the paragraphs!
77 Row * tmprow = firstrow;
79 tmprow = firstrow->next();
83 lastrow = refresh_row = need_break_row = 0;
85 copylayouttype.erase();
86 number_of_rows = first = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
94 insertParagraph(bview, par, lastrow);
97 setCursorIntern(bview, firstrow->par(), 0);
98 selection.cursor = cursor;
104 // Delete all rows, this does not touch the paragraphs!
105 Row * tmprow = firstrow;
107 tmprow = firstrow->next();
116 LyXFont const realizeFont(LyXFont const & font,
120 LyXFont tmpfont(font);
121 Paragraph::depth_type par_depth = par->getDepth();
123 // Resolve against environment font information
124 while (par && par_depth && !tmpfont.resolved()) {
125 par = par->outerHook();
127 #ifndef INHERIT_LANGUAGE
128 tmpfont.realize(textclasslist[buf->params.textclass][
129 par->layout()].font);
131 tmpfont.realize(textclasslist.
132 Style(buf->params.textclass,
134 buf->params.language);
136 par_depth = par->getDepth();
140 #ifndef INHERIT_LANGUAGE
141 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
143 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
144 buf->params.language);
153 // Gets the fully instantiated font at a given position in a paragraph
154 // Basically the same routine as Paragraph::getFont() in paragraph.C.
155 // The difference is that this one is used for displaying, and thus we
156 // are allowed to make cosmetic improvements. For instance make footnotes
158 // If position is -1, we get the layout font of the paragraph.
159 // If position is -2, we get the font of the manual label of the paragraph.
160 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
163 lyx::Assert(pos >= 0);
165 LyXLayout const & layout =
166 textclasslist[buf->params.textclass][par->layout()];
168 Paragraph::depth_type par_depth = par->getDepth();
169 // We specialize the 95% common case:
171 if (layout.labeltype == LABEL_MANUAL
172 && pos < beginningOfMainBody(buf, par)) {
174 LyXFont f = par->getFontSettings(buf->params, pos);
176 par->inInset()->getDrawFont(f);
177 #ifndef INHERIT_LANGUAGE
178 return f.realize(layout.reslabelfont);
180 return f.realize(layout.reslabelfont, buf->params.language);
183 LyXFont f = par->getFontSettings(buf->params, pos);
185 par->inInset()->getDrawFont(f);
186 #ifndef INHERIT_LANGUAGE
187 return f.realize(layout.resfont);
189 return f.realize(layout.resfont, buf->params.language);
194 // The uncommon case need not be optimized as much
198 if (pos < beginningOfMainBody(buf, par)) {
200 layoutfont = layout.labelfont;
203 layoutfont = layout.font;
206 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
207 #ifndef INHERIT_LANGUAGE
208 tmpfont.realize(layoutfont);
210 tmpfont.realize(layoutfont, buf->params.language);
213 par->inInset()->getDrawFont(tmpfont);
215 return realizeFont(tmpfont, buf, par);
219 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
221 LyXLayout const & layout =
222 textclasslist[buf->params.textclass][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 const & layout =
237 textclasslist[buf->params.textclass][par->layout()];
239 Paragraph::depth_type par_depth = par->getDepth();
242 return layout.reslabelfont;
245 return realizeFont(layout.labelfont, buf, par);
249 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
250 pos_type pos, LyXFont const & fnt,
253 Buffer const * buf = bv->buffer();
254 LyXFont font = getFont(buf, par, pos);
255 font.update(fnt, buf->params.language, toggleall);
256 // Let the insets convert their font
257 if (par->isInset(pos)) {
258 Inset * inset = par->getInset(pos);
259 if (isEditableInset(inset)) {
260 UpdatableInset * uinset =
261 static_cast<UpdatableInset *>(inset);
262 uinset->setFont(bv, fnt, toggleall, true);
266 // Plug thru to version below:
267 setCharFont(buf, par, pos, font);
271 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
272 pos_type pos, LyXFont const & fnt)
276 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
277 LyXLayout const & layout = tclass[par->layout()];
279 // Get concrete layout font to reduce against
282 if (pos < beginningOfMainBody(buf, par))
283 layoutfont = layout.labelfont;
285 layoutfont = layout.font;
287 // Realize against environment font information
288 if (par->getDepth()) {
289 Paragraph * tp = par;
290 while (!layoutfont.resolved() && tp && tp->getDepth()) {
291 tp = tp->outerHook();
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(tclass[tp->layout()].font);
296 layoutfont.realize(textclasslist.
297 Style(buf->params.textclass,
299 buf->params.language);
304 #ifndef INHERIT_LANGUAGE
305 layoutfont.realize(tclass.defaultfont());
307 layoutfont.realize(tclass.defaultfont(), buf->params.language);
310 // Now, reduce font against full layout font
311 font.reduce(layoutfont);
313 par->setFont(pos, font);
317 // inserts a new row behind the specified row, increments
318 // the touched counters
319 void LyXText::insertRow(Row * row, Paragraph * par,
322 Row * tmprow = new Row;
325 tmprow->next(firstrow);
328 tmprow->previous(row);
329 tmprow->next(row->next());
334 tmprow->next()->previous(tmprow);
336 if (tmprow->previous())
337 tmprow->previous()->next(tmprow);
349 // removes the row and reset the touched counters
350 void LyXText::removeRow(Row * row) const
352 Row * row_prev = row->previous();
354 row->next()->previous(row_prev);
356 firstrow = row->next();
357 // lyx::Assert(firstrow);
359 row_prev->next(row->next());
361 if (row == lastrow) {
362 lyx::Assert(!row->next());
365 if (refresh_row == row) {
366 refresh_row = row_prev ? row_prev : row->next();
367 // what about refresh_y, refresh_height
370 height -= row->height(); // the text becomes smaller
373 --number_of_rows; // one row less
377 // remove all following rows of the paragraph of the specified row.
378 void LyXText::removeParagraph(Row * row) const
380 Paragraph * tmppar = row->par();
384 while (row && row->par() == tmppar) {
385 tmprow = row->next();
392 // insert the specified paragraph behind the specified row
393 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
396 insertRow(row, par, 0); /* insert a new row, starting
399 setCounter(bview->buffer(), par); // set the counters
401 // and now append the whole paragraph behind the new row
404 appendParagraph(bview, firstrow);
406 row->next()->height(0);
407 appendParagraph(bview, row->next());
412 Inset * LyXText::getInset() const
415 if (cursor.pos() == 0 && cursor.par()->bibkey) {
416 inset = cursor.par()->bibkey;
417 } else if (cursor.pos() < cursor.par()->size()
418 && cursor.par()->isInset(cursor.pos())) {
419 inset = cursor.par()->getInset(cursor.pos());
425 void LyXText::toggleInset(BufferView * bview)
427 Inset * inset = getInset();
428 // is there an editable inset at cursor position?
429 if (!isEditableInset(inset)) {
430 // No, try to see if we are inside a collapsable inset
431 if (inset_owner && inset_owner->owner()
432 && inset_owner->owner()->isOpen()) {
433 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
434 inset_owner->owner()->close(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 const & layout =
461 textclasslist[buf->params.textclass][par->layout()];
464 for (pos_type pos = 0; pos < par->size(); ++pos) {
465 if (pos < beginningOfMainBody(buf, par))
466 layoutfont = layout.labelfont;
468 layoutfont = layout.font;
470 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
471 tmpfont.reduce(layoutfont);
472 par->setFont(pos, tmpfont);
477 Paragraph * LyXText::setLayout(BufferView * bview,
478 LyXCursor & cur, LyXCursor & sstart_cur,
479 LyXCursor & send_cur,
480 string const & layout)
482 Paragraph * endpar = send_cur.par()->next();
483 Paragraph * undoendpar = endpar;
485 if (endpar && endpar->getDepth()) {
486 while (endpar && endpar->getDepth()) {
487 endpar = endpar->next();
491 endpar = endpar->next(); // because of parindents etc.
494 setUndo(bview, Undo::EDIT,
495 sstart_cur.par(), undoendpar);
497 // ok we have a selection. This is always between sstart_cur
498 // and sel_end cursor
501 LyXLayout const & lyxlayout =
502 textclasslist[bview->buffer()->params.textclass][layout];
505 cur.par()->applyLayout(layout);
506 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
507 Paragraph * fppar = cur.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 cur.par()->setLabelWidthString(lyxlayout.labelstring());
516 if (lyxlayout.labeltype != LABEL_BIBLIO
518 delete fppar->bibkey;
521 if (cur.par() != send_cur.par())
522 cur.par(cur.par()->next());
523 } while (cur.par() != send_cur.par());
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;
592 LyXTextClass const & tclass =
593 textclasslist[bview->buffer()->params.textclass];
596 // NOTE: you can't change the depth of a bibliography entry
597 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
598 Paragraph * prev = cursor.par()->previous();
601 if (cursor.par()->getDepth()
602 < prev->getMaxDepthAfter(bview->buffer())){
603 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
604 anything_changed = true;
608 if (cursor.par() == selection.end.par())
610 cursor.par(cursor.par()->next());
613 // if nothing changed set all depth to 0
614 if (!anything_changed) {
615 cursor = selection.start;
616 while (cursor.par() != selection.end.par()) {
617 cursor.par()->params().depth(0);
618 cursor.par(cursor.par()->next());
620 cursor.par()->params().depth(0);
623 redoParagraphs(bview, selection.start, endpar);
625 // we have to reset the selection, because the
626 // geometry could have changed
627 setCursor(bview, selection.start.par(), selection.start.pos());
628 selection.cursor = cursor;
629 setCursor(bview, selection.end.par(), selection.end.pos());
630 updateCounters(bview, cursor.row());
633 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
637 // decrement depth over selection and
638 // make a total rebreak of those paragraphs
639 void LyXText::decDepth(BufferView * bview)
641 // if there is no selection just set the layout
642 // of the current paragraph
643 if (!selection.set()) {
644 selection.start = cursor; // dummy selection
645 selection.end = cursor;
647 Paragraph * endpar = selection.end.par()->next();
648 Paragraph * undoendpar = endpar;
650 if (endpar && endpar->getDepth()) {
651 while (endpar && endpar->getDepth()) {
652 endpar = endpar->next();
656 endpar = endpar->next(); // because of parindents etc.
659 setUndo(bview, Undo::EDIT,
660 selection.start.par(), undoendpar);
662 LyXCursor tmpcursor = cursor; // store the current cursor
664 // ok we have a selection. This is always between sel_start_cursor
665 // and sel_end cursor
666 cursor = selection.start;
669 if (cursor.par()->params().depth()) {
670 cursor.par()->params()
671 .depth(cursor.par()->params().depth() - 1);
673 if (cursor.par() == selection.end.par()) {
676 cursor.par(cursor.par()->next());
679 redoParagraphs(bview, selection.start, endpar);
681 // we have to reset the selection, because the
682 // geometry could have changed
683 setCursor(bview, selection.start.par(),
684 selection.start.pos());
685 selection.cursor = cursor;
686 setCursor(bview, selection.end.par(), selection.end.pos());
687 updateCounters(bview, cursor.row());
690 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
694 // set font over selection and make a total rebreak of those paragraphs
695 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
697 // if there is no selection just set the current_font
698 if (!selection.set()) {
699 // Determine basis font
701 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
703 layoutfont = getLabelFont(bview->buffer(),
706 layoutfont = getLayoutFont(bview->buffer(),
709 // Update current font
710 real_current_font.update(font,
711 bview->buffer()->params.language,
714 // Reduce to implicit settings
715 current_font = real_current_font;
716 current_font.reduce(layoutfont);
717 // And resolve it completely
718 #ifndef INHERIT_LANGUAGE
719 real_current_font.realize(layoutfont);
721 real_current_font.realize(layoutfont,
722 bview->buffer()->params.language);
727 LyXCursor tmpcursor = cursor; // store the current cursor
729 // ok we have a selection. This is always between sel_start_cursor
730 // and sel_end cursor
732 setUndo(bview, Undo::EDIT,
733 selection.start.par(), selection.end.par()->next());
735 cursor = selection.start;
736 while (cursor.par() != selection.end.par() ||
737 (cursor.pos() < selection.end.pos()))
739 if (cursor.pos() < cursor.par()->size()) {
740 // an open footnote should behave
742 setCharFont(bview, cursor.par(), cursor.pos(),
744 cursor.pos(cursor.pos() + 1);
747 cursor.par(cursor.par()->next());
752 redoParagraphs(bview, selection.start, selection.end.par()->next());
754 // we have to reset the selection, because the
755 // geometry could have changed, but we keep
756 // it for user convenience
757 setCursor(bview, selection.start.par(), selection.start.pos());
758 selection.cursor = cursor;
759 setCursor(bview, selection.end.par(), selection.end.pos());
761 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
762 tmpcursor.boundary());
766 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
768 Row * tmprow = cur.row();
769 int y = cur.y() - tmprow->baseline();
771 setHeightOfRow(bview, tmprow);
773 while (tmprow->previous()
774 && tmprow->previous()->par() == tmprow->par()) {
775 tmprow = tmprow->previous();
776 y -= tmprow->height();
777 setHeightOfRow(bview, tmprow);
780 // we can set the refreshing parameters now
781 status(bview, LyXText::NEED_MORE_REFRESH);
783 refresh_row = tmprow;
784 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
788 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
790 Row * tmprow = cur.row();
792 int y = cur.y() - tmprow->baseline();
793 setHeightOfRow(bview, tmprow);
795 while (tmprow->previous()
796 && tmprow->previous()->par() == tmprow->par()) {
797 tmprow = tmprow->previous();
798 y -= tmprow->height();
801 // we can set the refreshing parameters now
802 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
804 refresh_row = tmprow;
806 status(bview, LyXText::NEED_MORE_REFRESH);
807 setCursor(bview, cur.par(), cur.pos());
811 // deletes and inserts again all paragaphs between the cursor
812 // and the specified par
813 // This function is needed after SetLayout and SetFont etc.
814 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
815 Paragraph const * endpar) const
818 Paragraph * tmppar = 0;
819 Paragraph * first_phys_par = 0;
821 Row * tmprow = cur.row();
823 int y = cur.y() - tmprow->baseline();
825 if (!tmprow->previous()) {
826 // a trick/hack for UNDO
827 // This is needed because in an UNDO/REDO we could have changed
828 // the ownerParagrah() so the paragraph inside the row is NOT
829 // my really first par anymore. Got it Lars ;) (Jug 20011206)
830 first_phys_par = ownerParagraph();
832 first_phys_par = tmprow->par();
833 while (tmprow->previous()
834 && tmprow->previous()->par() == first_phys_par)
836 tmprow = tmprow->previous();
837 y -= tmprow->height();
841 // we can set the refreshing parameters now
842 status(bview, LyXText::NEED_MORE_REFRESH);
844 refresh_row = tmprow->previous(); /* the real refresh row will
845 be deleted, so I store
849 tmppar = tmprow->next()->par();
852 while (tmprow->next() && tmppar != endpar) {
853 removeRow(tmprow->next());
854 if (tmprow->next()) {
855 tmppar = tmprow->next()->par();
861 // remove the first one
862 tmprow2 = tmprow; /* this is because tmprow->previous()
864 tmprow = tmprow->previous();
867 tmppar = first_phys_par;
871 insertParagraph(bview, tmppar, tmprow);
875 while (tmprow->next()
876 && tmprow->next()->par() == tmppar) {
877 tmprow = tmprow->next();
879 tmppar = tmppar->next();
881 } while (tmppar && tmppar != endpar);
883 // this is because of layout changes
885 refresh_y -= refresh_row->height();
886 setHeightOfRow(bview, refresh_row);
888 refresh_row = firstrow;
890 setHeightOfRow(bview, refresh_row);
893 if (tmprow && tmprow->next())
894 setHeightOfRow(bview, tmprow->next());
898 bool LyXText::fullRebreak(BufferView * bview)
904 if (need_break_row) {
905 breakAgain(bview, need_break_row);
913 // important for the screen
916 /* the cursor set functions have a special mechanism. When they
917 * realize, that you left an empty paragraph, they will delete it.
918 * They also delete the corresponding row */
920 // need the selection cursor:
921 void LyXText::setSelection(BufferView * bview)
923 bool const lsel = selection.set();
925 if (!selection.set()) {
926 last_sel_cursor = selection.cursor;
927 selection.start = selection.cursor;
928 selection.end = selection.cursor;
933 // first the toggling area
934 if (cursor.y() < last_sel_cursor.y()
935 || (cursor.y() == last_sel_cursor.y()
936 && cursor.x() < last_sel_cursor.x())) {
937 toggle_end_cursor = last_sel_cursor;
938 toggle_cursor = cursor;
940 toggle_end_cursor = cursor;
941 toggle_cursor = last_sel_cursor;
944 last_sel_cursor = cursor;
946 // and now the whole selection
948 if (selection.cursor.par() == cursor.par())
949 if (selection.cursor.pos() < cursor.pos()) {
950 selection.end = cursor;
951 selection.start = selection.cursor;
953 selection.end = selection.cursor;
954 selection.start = cursor;
956 else if (selection.cursor.y() < cursor.y() ||
957 (selection.cursor.y() == cursor.y()
958 && selection.cursor.x() < cursor.x())) {
959 selection.end = cursor;
960 selection.start = selection.cursor;
963 selection.end = selection.cursor;
964 selection.start = cursor;
967 // a selection with no contents is not a selection
968 if (selection.start.par() == selection.end.par() &&
969 selection.start.pos() == selection.end.pos())
970 selection.set(false);
972 if (inset_owner && (selection.set() || lsel))
973 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
977 string const LyXText::selectionAsString(Buffer const * buffer,
980 if (!selection.set()) return string();
983 // Special handling if the whole selection is within one paragraph
984 if (selection.start.par() == selection.end.par()) {
985 result += selection.start.par()->asString(buffer,
986 selection.start.pos(),
992 // The selection spans more than one paragraph
994 // First paragraph in selection
995 result += selection.start.par()->asString(buffer,
996 selection.start.pos(),
997 selection.start.par()->size(),
1001 // The paragraphs in between (if any)
1002 LyXCursor tmpcur(selection.start);
1003 tmpcur.par(tmpcur.par()->next());
1004 while (tmpcur.par() != selection.end.par()) {
1005 result += tmpcur.par()->asString(buffer, 0,
1006 tmpcur.par()->size(),
1008 tmpcur.par(tmpcur.par()->next());
1011 // Last paragraph in selection
1012 result += selection.end.par()->asString(buffer, 0,
1013 selection.end.pos(), label);
1019 void LyXText::clearSelection() const
1021 selection.set(false);
1022 selection.mark(false);
1023 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1027 void LyXText::cursorHome(BufferView * bview) const
1029 setCursor(bview, cursor.par(), cursor.row()->pos());
1033 void LyXText::cursorEnd(BufferView * bview) const
1035 if (!cursor.row()->next()
1036 || cursor.row()->next()->par() != cursor.row()->par()) {
1037 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1039 if (cursor.par()->size() &&
1040 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1041 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1042 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1044 setCursor(bview,cursor.par(),
1045 rowLast(cursor.row()) + 1);
1051 void LyXText::cursorTop(BufferView * bview) const
1053 while (cursor.par()->previous())
1054 cursor.par(cursor.par()->previous());
1055 setCursor(bview, cursor.par(), 0);
1059 void LyXText::cursorBottom(BufferView * bview) const
1061 while (cursor.par()->next())
1062 cursor.par(cursor.par()->next());
1063 setCursor(bview, cursor.par(), cursor.par()->size());
1067 void LyXText::toggleFree(BufferView * bview,
1068 LyXFont const & font, bool toggleall)
1070 // If the mask is completely neutral, tell user
1071 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1072 // Could only happen with user style
1073 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1077 // Try implicit word selection
1078 // If there is a change in the language the implicit word selection
1080 LyXCursor resetCursor = cursor;
1081 bool implicitSelection = (font.language() == ignore_language
1082 && font.number() == LyXFont::IGNORE)
1083 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1086 setFont(bview, font, toggleall);
1088 // Implicit selections are cleared afterwards
1089 //and cursor is set to the original position.
1090 if (implicitSelection) {
1092 cursor = resetCursor;
1093 setCursor(bview, cursor.par(), cursor.pos());
1094 selection.cursor = cursor;
1097 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1101 string LyXText::getStringToIndex(BufferView * bview)
1105 // Try implicit word selection
1106 // If there is a change in the language the implicit word selection
1108 LyXCursor resetCursor = cursor;
1109 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1111 if (!selection.set()) {
1112 bview->owner()->message(_("Nothing to index!"));
1115 if (selection.start.par() != selection.end.par()) {
1116 bview->owner()->message(_("Cannot index more than one paragraph!"));
1120 idxstring = selectionAsString(bview->buffer(), false);
1122 // Implicit selections are cleared afterwards
1123 //and cursor is set to the original position.
1124 if (implicitSelection) {
1126 cursor = resetCursor;
1127 setCursor(bview, cursor.par(), cursor.pos());
1128 selection.cursor = cursor;
1134 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1135 Paragraph const * par) const
1137 if (textclasslist[buf->params.textclass][
1138 par->layout()].labeltype != LABEL_MANUAL)
1141 return par->beginningOfMainBody();
1145 /* the DTP switches for paragraphs. LyX will store them in the
1146 * first physicla paragraph. When a paragraph is broken, the top settings
1147 * rest, the bottom settings are given to the new one. So I can make shure,
1148 * they do not duplicate themself and you cannnot make dirty things with
1151 void LyXText::setParagraph(BufferView * bview,
1152 bool line_top, bool line_bottom,
1153 bool pagebreak_top, bool pagebreak_bottom,
1154 VSpace const & space_top,
1155 VSpace const & space_bottom,
1156 Spacing const & spacing,
1158 string labelwidthstring,
1161 LyXCursor tmpcursor = cursor;
1162 if (!selection.set()) {
1163 selection.start = cursor;
1164 selection.end = cursor;
1167 // make sure that the depth behind the selection are restored, too
1168 Paragraph * endpar = selection.end.par()->next();
1169 Paragraph * undoendpar = endpar;
1171 if (endpar && endpar->getDepth()) {
1172 while (endpar && endpar->getDepth()) {
1173 endpar = endpar->next();
1174 undoendpar = endpar;
1178 // because of parindents etc.
1179 endpar = endpar->next();
1182 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1185 Paragraph * tmppar = selection.end.par();
1186 LyXTextClass const & tclass =
1187 textclasslist[bview->buffer()->params.textclass];
1189 while (tmppar != selection.start.par()->previous()) {
1190 setCursor(bview, tmppar, 0);
1191 status(bview, LyXText::NEED_MORE_REFRESH);
1192 refresh_row = cursor.row();
1193 refresh_y = cursor.y() - cursor.row()->baseline();
1194 cursor.par()->params().lineTop(line_top);
1195 cursor.par()->params().lineBottom(line_bottom);
1196 cursor.par()->params().pagebreakTop(pagebreak_top);
1197 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1198 cursor.par()->params().spaceTop(space_top);
1199 cursor.par()->params().spaceBottom(space_bottom);
1200 cursor.par()->params().spacing(spacing);
1201 // does the layout allow the new alignment?
1202 LyXLayout const & layout = tclass[cursor.par()->layout()];
1204 if (align == LYX_ALIGN_LAYOUT)
1205 align = layout.align;
1206 if (align & layout.alignpossible) {
1207 if (align == layout.align)
1208 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1210 cursor.par()->params().align(align);
1212 cursor.par()->setLabelWidthString(labelwidthstring);
1213 cursor.par()->params().noindent(noindent);
1214 tmppar = cursor.par()->previous();
1217 redoParagraphs(bview, selection.start, endpar);
1220 setCursor(bview, selection.start.par(), selection.start.pos());
1221 selection.cursor = cursor;
1222 setCursor(bview, selection.end.par(), selection.end.pos());
1223 setSelection(bview);
1224 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1226 bview->updateInset(inset_owner, true);
1230 char loweralphaCounter(int n)
1232 if (n < 1 || n > 26)
1242 char alphaCounter(int n)
1244 if (n < 1 || n > 26)
1252 char hebrewCounter(int n)
1254 static const char hebrew[22] = {
1255 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1256 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1257 '÷', 'ø', 'ù', 'ú'
1259 if (n < 1 || n > 22)
1267 string const romanCounter(int n)
1269 static char const * roman[20] = {
1270 "i", "ii", "iii", "iv", "v",
1271 "vi", "vii", "viii", "ix", "x",
1272 "xi", "xii", "xiii", "xiv", "xv",
1273 "xvi", "xvii", "xviii", "xix", "xx"
1275 if (n < 1 || n > 20)
1284 // set the counter of a paragraph. This includes the labels
1285 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1287 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1288 LyXLayout const & layout = textclass[par->layout()];
1291 // copy the prev-counters to this one,
1292 // unless this is the first paragraph
1293 if (par->previous()) {
1294 for (int i = 0; i < 10; ++i) {
1295 par->setCounter(i, par->previous()->getFirstCounter(i));
1297 par->params().appendix(par->previous()->params().appendix());
1298 if (!par->params().appendix() && par->params().startOfAppendix()) {
1299 par->params().appendix(true);
1300 for (int i = 0; i < 10; ++i) {
1301 par->setCounter(i, 0);
1304 par->enumdepth = par->previous()->enumdepth;
1305 par->itemdepth = par->previous()->itemdepth;
1307 for (int i = 0; i < 10; ++i) {
1308 par->setCounter(i, 0);
1310 par->params().appendix(par->params().startOfAppendix());
1315 /* Maybe we have to increment the enumeration depth.
1316 * BUT, enumeration in a footnote is considered in isolation from its
1317 * surrounding paragraph so don't increment if this is the
1318 * first line of the footnote
1319 * AND, bibliographies can't have their depth changed ie. they
1320 * are always of depth 0
1323 && par->previous()->getDepth() < par->getDepth()
1324 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1325 && par->enumdepth < 3
1326 && layout.labeltype != LABEL_BIBLIO) {
1330 // Maybe we have to decrement the enumeration depth, see note above
1332 && par->previous()->getDepth() > par->getDepth()
1333 && layout.labeltype != LABEL_BIBLIO) {
1334 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1335 par->setCounter(6 + par->enumdepth,
1336 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1337 /* reset the counters.
1338 * A depth change is like a breaking layout
1340 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1341 par->setCounter(i, 0);
1344 if (!par->params().labelString().empty()) {
1345 par->params().labelString(string());
1348 if (layout.margintype == MARGIN_MANUAL) {
1349 if (par->params().labelWidthString().empty()) {
1350 par->setLabelWidthString(layout.labelstring());
1353 par->setLabelWidthString(string());
1356 // is it a layout that has an automatic label?
1357 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1359 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1360 if (i >= 0 && i<= buf->params.secnumdepth) {
1361 par->incCounter(i); // increment the counter
1363 // Is there a label? Useful for Chapter layout
1364 if (!par->params().appendix()) {
1365 if (!layout.labelstring().empty())
1366 par->params().labelString(layout.labelstring());
1368 par->params().labelString(string());
1370 if (!layout.labelstring_appendix().empty())
1371 par->params().labelString(layout.labelstring_appendix());
1373 par->params().labelString(string());
1378 if (!par->params().appendix()) {
1379 switch (2 * LABEL_COUNTER_CHAPTER -
1380 textclass.maxcounter() + i) {
1381 case LABEL_COUNTER_CHAPTER:
1382 s << par->getCounter(i);
1384 case LABEL_COUNTER_SECTION:
1385 s << par->getCounter(i - 1) << '.'
1386 << par->getCounter(i);
1388 case LABEL_COUNTER_SUBSECTION:
1389 s << par->getCounter(i - 2) << '.'
1390 << par->getCounter(i - 1) << '.'
1391 << par->getCounter(i);
1393 case LABEL_COUNTER_SUBSUBSECTION:
1394 s << par->getCounter(i - 3) << '.'
1395 << par->getCounter(i - 2) << '.'
1396 << par->getCounter(i - 1) << '.'
1397 << par->getCounter(i);
1400 case LABEL_COUNTER_PARAGRAPH:
1401 s << par->getCounter(i - 4) << '.'
1402 << par->getCounter(i - 3) << '.'
1403 << par->getCounter(i - 2) << '.'
1404 << par->getCounter(i - 1) << '.'
1405 << par->getCounter(i);
1407 case LABEL_COUNTER_SUBPARAGRAPH:
1408 s << par->getCounter(i - 5) << '.'
1409 << par->getCounter(i - 4) << '.'
1410 << par->getCounter(i - 3) << '.'
1411 << par->getCounter(i - 2) << '.'
1412 << par->getCounter(i - 1) << '.'
1413 << par->getCounter(i);
1417 // Can this ever be reached? And in the
1418 // case it is, how can this be correct?
1420 s << par->getCounter(i) << '.';
1423 } else { // appendix
1424 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1425 case LABEL_COUNTER_CHAPTER:
1426 if (par->isRightToLeftPar(buf->params))
1427 s << hebrewCounter(par->getCounter(i));
1429 s << alphaCounter(par->getCounter(i));
1431 case LABEL_COUNTER_SECTION:
1432 if (par->isRightToLeftPar(buf->params))
1433 s << hebrewCounter(par->getCounter(i - 1));
1435 s << alphaCounter(par->getCounter(i - 1));
1438 << par->getCounter(i);
1441 case LABEL_COUNTER_SUBSECTION:
1442 if (par->isRightToLeftPar(buf->params))
1443 s << hebrewCounter(par->getCounter(i - 2));
1445 s << alphaCounter(par->getCounter(i - 2));
1448 << par->getCounter(i-1) << '.'
1449 << par->getCounter(i);
1452 case LABEL_COUNTER_SUBSUBSECTION:
1453 if (par->isRightToLeftPar(buf->params))
1454 s << hebrewCounter(par->getCounter(i-3));
1456 s << alphaCounter(par->getCounter(i-3));
1459 << par->getCounter(i-2) << '.'
1460 << par->getCounter(i-1) << '.'
1461 << par->getCounter(i);
1464 case LABEL_COUNTER_PARAGRAPH:
1465 if (par->isRightToLeftPar(buf->params))
1466 s << hebrewCounter(par->getCounter(i-4));
1468 s << alphaCounter(par->getCounter(i-4));
1471 << par->getCounter(i-3) << '.'
1472 << par->getCounter(i-2) << '.'
1473 << par->getCounter(i-1) << '.'
1474 << par->getCounter(i);
1477 case LABEL_COUNTER_SUBPARAGRAPH:
1478 if (par->isRightToLeftPar(buf->params))
1479 s << hebrewCounter(par->getCounter(i-5));
1481 s << alphaCounter(par->getCounter(i-5));
1484 << par->getCounter(i-4) << '.'
1485 << par->getCounter(i-3) << '.'
1486 << par->getCounter(i-2) << '.'
1487 << par->getCounter(i-1) << '.'
1488 << par->getCounter(i);
1492 // Can this ever be reached? And in the
1493 // case it is, how can this be correct?
1495 s << par->getCounter(i) << '.';
1501 par->params().labelString(par->params().labelString() +s.str().c_str());
1502 // We really want to remove the c_str as soon as
1505 for (i++; i < 10; ++i) {
1506 // reset the following counters
1507 par->setCounter(i, 0);
1509 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1510 for (i++; i < 10; ++i) {
1511 // reset the following counters
1512 par->setCounter(i, 0);
1514 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1515 par->incCounter(i + par->enumdepth);
1516 int number = par->getCounter(i + par->enumdepth);
1520 switch (par->enumdepth) {
1522 if (par->isRightToLeftPar(buf->params))
1524 << hebrewCounter(number)
1528 << loweralphaCounter(number)
1532 if (par->isRightToLeftPar(buf->params))
1533 s << '.' << romanCounter(number);
1535 s << romanCounter(number) << '.';
1538 if (par->isRightToLeftPar(buf->params))
1540 << alphaCounter(number);
1542 s << alphaCounter(number)
1546 if (par->isRightToLeftPar(buf->params))
1553 par->params().labelString(s.str().c_str());
1555 for (i += par->enumdepth + 1; i < 10; ++i) {
1556 // reset the following counters
1557 par->setCounter(i, 0);
1561 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1562 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1564 int number = par->getCounter(i);
1566 InsetCommandParams p("bibitem" );
1567 par->bibkey = new InsetBibKey(p);
1569 par->bibkey->setCounter(number);
1570 par->params().labelString(layout.labelstring());
1572 // In biblio should't be following counters but...
1574 string s = layout.labelstring();
1576 // the caption hack:
1577 if (layout.labeltype == LABEL_SENSITIVE) {
1578 bool isOK (par->inInset() && par->inInset()->owner() &&
1579 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1582 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1584 = floatList.getType(tmp->type());
1585 // We should get the correct number here too.
1586 s = fl.name() + " #:";
1588 /* par->SetLayout(0);
1589 s = layout->labelstring; */
1590 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1591 ? " :úåòîùî øñç" : "Senseless: ";
1594 par->params().labelString(s);
1596 /* reset the enumeration counter. They are always resetted
1597 * when there is any other layout between */
1598 for (int i = 6 + par->enumdepth; i < 10; ++i)
1599 par->setCounter(i, 0);
1604 // Updates all counters BEHIND the row. Changed paragraphs
1605 // with a dynamic left margin will be rebroken.
1606 void LyXText::updateCounters(BufferView * bview, Row * row) const
1614 par = row->par()->next();
1618 while (row->par() != par)
1621 setCounter(bview->buffer(), par);
1623 // now check for the headline layouts. remember that they
1624 // have a dynamic left margin
1625 LyXTextClass const & tclass =
1626 textclasslist[bview->buffer()->params.textclass];
1627 LyXLayout const & layout = tclass[par->layout()];
1629 if (layout.margintype == MARGIN_DYNAMIC
1630 || layout.labeltype == LABEL_SENSITIVE) {
1631 // Rebreak the paragraph
1632 removeParagraph(row);
1633 appendParagraph(bview, row);
1640 void LyXText::insertInset(BufferView * bview, Inset * inset)
1642 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1644 // I don't know if this is necessary here (Jug 20020102)
1645 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1646 cursor.par()->insertInset(cursor.pos(), inset);
1647 // Just to rebreak and refresh correctly.
1648 // The character will not be inserted a second time
1649 insertChar(bview, Paragraph::META_INSET);
1651 // If we enter a highly editable inset the cursor should be to before
1652 // the inset. This couldn't happen before as Undo was not handled inside
1653 // inset now after the Undo LyX tries to call inset->Edit(...) again
1654 // and cannot do this as the cursor is behind the inset and GetInset
1655 // does not return the inset!
1656 if (isHighlyEditableInset(inset)) {
1657 cursorLeft(bview, true);
1663 void LyXText::copyEnvironmentType()
1665 copylayouttype = cursor.par()->layout();
1669 void LyXText::pasteEnvironmentType(BufferView * bview)
1671 setLayout(bview, copylayouttype);
1675 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1677 // Stuff what we got on the clipboard. Even if there is no selection.
1679 // There is a problem with having the stuffing here in that the
1680 // larger the selection the slower LyX will get. This can be
1681 // solved by running the line below only when the selection has
1682 // finished. The solution used currently just works, to make it
1683 // faster we need to be more clever and probably also have more
1684 // calls to stuffClipboard. (Lgb)
1685 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1687 // This doesn't make sense, if there is no selection
1688 if (!selection.set())
1691 // OK, we have a selection. This is always between selection.start
1692 // and selection.end
1694 // make sure that the depth behind the selection are restored, too
1695 Paragraph * endpar = selection.end.par()->next();
1696 Paragraph * undoendpar = endpar;
1698 if (endpar && endpar->getDepth()) {
1699 while (endpar && endpar->getDepth()) {
1700 endpar = endpar->next();
1701 undoendpar = endpar;
1703 } else if (endpar) {
1704 endpar = endpar->next(); // because of parindents etc.
1707 setUndo(bview, Undo::DELETE,
1708 selection.start.par(), undoendpar);
1710 // there are two cases: cut only within one paragraph or
1711 // more than one paragraph
1712 if (selection.start.par() == selection.end.par()) {
1713 // only within one paragraph
1714 endpar = selection.end.par();
1715 int pos = selection.end.pos();
1716 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1717 selection.start.pos(), pos,
1718 bview->buffer()->params.textclass,
1720 selection.end.pos(pos);
1722 endpar = selection.end.par();
1723 int pos = selection.end.pos();
1724 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1725 selection.start.pos(), pos,
1726 bview->buffer()->params.textclass,
1729 selection.end.par(endpar);
1730 selection.end.pos(pos);
1731 cursor.pos(selection.end.pos());
1733 endpar = endpar->next();
1735 // sometimes necessary
1737 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1739 redoParagraphs(bview, selection.start, endpar);
1741 // cutSelection can invalidate the cursor so we need to set
1743 cursor = selection.start;
1745 // need a valid cursor. (Lgb)
1748 setCursor(bview, cursor.par(), cursor.pos());
1749 selection.cursor = cursor;
1750 updateCounters(bview, cursor.row());
1754 void LyXText::copySelection(BufferView * bview)
1756 // stuff the selection onto the X clipboard, from an explicit copy request
1757 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1759 // this doesnt make sense, if there is no selection
1760 if (!selection.set())
1763 // ok we have a selection. This is always between selection.start
1764 // and sel_end cursor
1766 // copy behind a space if there is one
1767 while (selection.start.par()->size() > selection.start.pos()
1768 && selection.start.par()->isLineSeparator(selection.start.pos())
1769 && (selection.start.par() != selection.end.par()
1770 || selection.start.pos() < selection.end.pos()))
1771 selection.start.pos(selection.start.pos() + 1);
1773 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1774 selection.start.pos(), selection.end.pos(),
1775 bview->buffer()->params.textclass);
1779 void LyXText::pasteSelection(BufferView * bview)
1781 // this does not make sense, if there is nothing to paste
1782 if (!CutAndPaste::checkPastePossible(cursor.par()))
1785 setUndo(bview, Undo::INSERT,
1786 cursor.par(), cursor.par()->next());
1789 Paragraph * actpar = cursor.par();
1790 int pos = cursor.pos();
1792 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1793 bview->buffer()->params.textclass);
1795 redoParagraphs(bview, cursor, endpar);
1797 setCursor(bview, cursor.par(), cursor.pos());
1800 setCursor(bview, actpar, pos);
1801 updateCounters(bview, cursor.row());
1805 // sets the selection over the number of characters of string, no check!!
1806 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1811 selection.cursor = cursor;
1812 for (string::size_type i = 0; i < str.length(); ++i)
1814 setSelection(bview);
1818 // simple replacing. The font of the first selected character is used
1819 void LyXText::replaceSelectionWithString(BufferView * bview,
1822 setCursorParUndo(bview);
1825 if (!selection.set()) { // create a dummy selection
1826 selection.end = cursor;
1827 selection.start = cursor;
1830 // Get font setting before we cut
1831 pos_type pos = selection.end.pos();
1832 LyXFont const font = selection.start.par()
1833 ->getFontSettings(bview->buffer()->params,
1834 selection.start.pos());
1836 // Insert the new string
1837 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1838 selection.end.par()->insertChar(pos, (*cit), font);
1842 // Cut the selection
1843 cutSelection(bview, true, false);
1849 // needed to insert the selection
1850 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1852 Paragraph * par = cursor.par();
1853 pos_type pos = cursor.pos();
1854 Paragraph * endpar = cursor.par()->next();
1856 setCursorParUndo(bview);
1858 // only to be sure, should not be neccessary
1861 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1863 redoParagraphs(bview, cursor, endpar);
1864 setCursor(bview, cursor.par(), cursor.pos());
1865 selection.cursor = cursor;
1866 setCursor(bview, par, pos);
1867 setSelection(bview);
1871 // turns double-CR to single CR, others where converted into one
1872 // blank. Then InsertStringAsLines is called
1873 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1875 string linestr(str);
1876 bool newline_inserted = false;
1877 for (string::size_type i = 0; i < linestr.length(); ++i) {
1878 if (linestr[i] == '\n') {
1879 if (newline_inserted) {
1880 // we know that \r will be ignored by
1881 // InsertStringA. Of course, it is a dirty
1882 // trick, but it works...
1883 linestr[i - 1] = '\r';
1887 newline_inserted = true;
1889 } else if (IsPrintable(linestr[i])) {
1890 newline_inserted = false;
1893 insertStringAsLines(bview, linestr);
1897 bool LyXText::gotoNextInset(BufferView * bview,
1898 vector<Inset::Code> const & codes,
1899 string const & contents) const
1901 LyXCursor res = cursor;
1904 if (res.pos() < res.par()->size() - 1) {
1905 res.pos(res.pos() + 1);
1907 res.par(res.par()->next());
1911 } while (res.par() &&
1912 !(res.par()->isInset(res.pos())
1913 && (inset = res.par()->getInset(res.pos())) != 0
1914 && find(codes.begin(), codes.end(), inset->lyxCode())
1916 && (contents.empty() ||
1917 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1921 setCursor(bview, res.par(), res.pos(), false);
1928 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1931 LyXCursor tmpcursor;
1935 Row * row = getRow(par, pos, y);
1937 // is there a break one row above
1938 if (row->previous() && row->previous()->par() == row->par()) {
1939 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1940 if (z >= row->pos()) {
1941 // set the dimensions of the row above
1942 y -= row->previous()->height();
1944 refresh_row = row->previous();
1945 status(bview, LyXText::NEED_MORE_REFRESH);
1947 breakAgain(bview, row->previous());
1949 // set the cursor again. Otherwise
1950 // dangling pointers are possible
1951 setCursor(bview, cursor.par(), cursor.pos(),
1952 false, cursor.boundary());
1953 selection.cursor = cursor;
1958 int const tmpheight = row->height();
1959 pos_type const tmplast = rowLast(row);
1963 breakAgain(bview, row);
1964 if (row->height() == tmpheight && rowLast(row) == tmplast)
1965 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1967 status(bview, LyXText::NEED_MORE_REFRESH);
1969 // check the special right address boxes
1970 if (textclasslist[bview->buffer()->params.textclass][
1971 par->layout()].margintype
1972 == MARGIN_RIGHT_ADDRESS_BOX)
1980 redoDrawingOfParagraph(bview, tmpcursor);
1983 // set the cursor again. Otherwise dangling pointers are possible
1984 // also set the selection
1986 if (selection.set()) {
1988 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1989 false, selection.cursor.boundary());
1990 selection.cursor = cursor;
1991 setCursorIntern(bview, selection.start.par(),
1992 selection.start.pos(),
1993 false, selection.start.boundary());
1994 selection.start = cursor;
1995 setCursorIntern(bview, selection.end.par(),
1996 selection.end.pos(),
1997 false, selection.end.boundary());
1998 selection.end = cursor;
1999 setCursorIntern(bview, last_sel_cursor.par(),
2000 last_sel_cursor.pos(),
2001 false, last_sel_cursor.boundary());
2002 last_sel_cursor = cursor;
2005 setCursorIntern(bview, cursor.par(), cursor.pos(),
2006 false, cursor.boundary());
2010 // returns false if inset wasn't found
2011 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2013 // first check the current paragraph
2014 int pos = cursor.par()->getPositionOfInset(inset);
2016 checkParagraph(bview, cursor.par(), pos);
2020 // check every paragraph
2022 Paragraph * par = ownerParagraph();
2024 pos = par->getPositionOfInset(inset);
2026 checkParagraph(bview, par, pos);
2036 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2038 bool setfont, bool boundary) const
2040 LyXCursor old_cursor = cursor;
2041 setCursorIntern(bview, par, pos, setfont, boundary);
2042 return deleteEmptyParagraphMechanism(bview, old_cursor);
2046 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2047 pos_type pos, bool boundary) const
2054 cur.boundary(boundary);
2056 // get the cursor y position in text
2058 Row * row = getRow(par, pos, y);
2059 // y is now the beginning of the cursor row
2060 y += row->baseline();
2061 // y is now the cursor baseline
2064 // now get the cursors x position
2066 float fill_separator;
2068 float fill_label_hfill;
2069 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2071 pos_type cursor_vpos = 0;
2072 pos_type last = rowLastPrintable(row);
2074 if (pos > last + 1) {
2075 // This shouldn't happen.
2078 } else if (pos < row->pos()) {
2083 if (last < row->pos())
2084 cursor_vpos = row->pos();
2085 else if (pos > last && !boundary)
2086 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2087 ? row->pos() : last + 1;
2088 else if (pos > row->pos() &&
2089 (pos > last || boundary))
2090 /// Place cursor after char at (logical) position pos - 1
2091 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2092 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2094 /// Place cursor before char at (logical) position pos
2095 cursor_vpos = (bidi_level(pos) % 2 == 0)
2096 ? log2vis(pos) : log2vis(pos) + 1;
2098 pos_type main_body =
2099 beginningOfMainBody(bview->buffer(), row->par());
2100 if ((main_body > 0) &&
2101 ((main_body-1 > last) ||
2102 !row->par()->isLineSeparator(main_body-1)))
2105 for (pos_type vpos = row->pos();
2106 vpos < cursor_vpos; ++vpos) {
2107 pos = vis2log(vpos);
2108 if (main_body > 0 && pos == main_body - 1) {
2109 x += fill_label_hfill +
2110 lyxfont::width(textclasslist[
2111 bview->buffer()->params.textclass][
2112 row->par()->layout()]
2114 getLabelFont(bview->buffer(), row->par()));
2115 if (row->par()->isLineSeparator(main_body-1))
2116 x -= singleWidth(bview, row->par(),main_body-1);
2118 if (hfillExpansion(bview->buffer(), row, pos)) {
2119 x += singleWidth(bview, row->par(), pos);
2120 if (pos >= main_body)
2123 x += fill_label_hfill;
2124 } else if (row->par()->isSeparator(pos)) {
2125 x += singleWidth(bview, row->par(), pos);
2126 if (pos >= main_body)
2127 x += fill_separator;
2129 x += singleWidth(bview, row->par(), pos);
2138 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2139 pos_type pos, bool setfont, bool boundary) const
2141 InsetText * it = static_cast<InsetText *>(par->inInset());
2143 if (it != inset_owner) {
2144 lyxerr << "InsetText is " << it << endl;
2145 lyxerr << "inset_owner is " << inset_owner << endl;
2146 #ifdef WITH_WARNINGS
2147 #warning I believe this code is wrong. (Lgb)
2148 #warning Jürgen, have a look at this. (Lgb)
2149 #warning Hmmm, I guess you are right but we
2150 #warning should verify when this is needed
2152 // Jürgen, would you like to have a look?
2153 // I guess we need to move the outer cursor
2154 // and open and lock the inset (bla bla bla)
2155 // stuff I don't know... so can you have a look?
2157 // I moved the lyxerr stuff in here so we can see if
2158 // this is actually really needed and where!
2160 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2165 setCursor(bview, cursor, par, pos, boundary);
2167 setCurrentFont(bview);
2171 void LyXText::setCurrentFont(BufferView * bview) const
2173 pos_type pos = cursor.pos();
2174 if (cursor.boundary() && pos > 0)
2178 if (pos == cursor.par()->size())
2180 else // potentional bug... BUG (Lgb)
2181 if (cursor.par()->isSeparator(pos)) {
2182 if (pos > cursor.row()->pos() &&
2183 bidi_level(pos) % 2 ==
2184 bidi_level(pos - 1) % 2)
2186 else if (pos + 1 < cursor.par()->size())
2192 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2193 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2195 if (cursor.pos() == cursor.par()->size() &&
2196 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2197 !cursor.boundary()) {
2198 Language const * lang =
2199 cursor.par()->getParLanguage(bview->buffer()->params);
2200 current_font.setLanguage(lang);
2201 current_font.setNumber(LyXFont::OFF);
2202 real_current_font.setLanguage(lang);
2203 real_current_font.setNumber(LyXFont::OFF);
2208 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2210 LyXCursor old_cursor = cursor;
2212 setCursorFromCoordinates(bview, cursor, x, y);
2213 setCurrentFont(bview);
2214 deleteEmptyParagraphMechanism(bview, old_cursor);
2218 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2221 // Get the row first.
2223 Row * row = getRowNearY(y);
2225 pos_type const column = getColumnNearX(bview, row, x, bound);
2226 cur.par(row->par());
2227 cur.pos(row->pos() + column);
2229 cur.y(y + row->baseline());
2231 cur.boundary(bound);
2235 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2237 if (cursor.pos() > 0) {
2238 bool boundary = cursor.boundary();
2239 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2240 if (!internal && !boundary &&
2241 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2242 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2243 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2244 Paragraph * par = cursor.par()->previous();
2245 setCursor(bview, par, par->size());
2250 void LyXText::cursorRight(BufferView * bview, bool internal) const
2252 if (!internal && cursor.boundary() &&
2253 !cursor.par()->isNewline(cursor.pos()))
2254 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2255 else if (cursor.pos() < cursor.par()->size()) {
2256 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2258 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2259 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2260 } else if (cursor.par()->next())
2261 setCursor(bview, cursor.par()->next(), 0);
2265 void LyXText::cursorUp(BufferView * bview) const
2267 setCursorFromCoordinates(bview, cursor.x_fix(),
2268 cursor.y() - cursor.row()->baseline() - 1);
2272 void LyXText::cursorDown(BufferView * bview) const
2274 setCursorFromCoordinates(bview, cursor.x_fix(),
2275 cursor.y() - cursor.row()->baseline()
2276 + cursor.row()->height() + 1);
2280 void LyXText::cursorUpParagraph(BufferView * bview) const
2282 if (cursor.pos() > 0) {
2283 setCursor(bview, cursor.par(), 0);
2285 else if (cursor.par()->previous()) {
2286 setCursor(bview, cursor.par()->previous(), 0);
2291 void LyXText::cursorDownParagraph(BufferView * bview) const
2293 if (cursor.par()->next()) {
2294 setCursor(bview, cursor.par()->next(), 0);
2296 setCursor(bview, cursor.par(), cursor.par()->size());
2300 // fix the cursor `cur' after a characters has been deleted at `where'
2301 // position. Called by deleteEmptyParagraphMechanism
2302 void LyXText::fixCursorAfterDelete(BufferView * bview,
2304 LyXCursor const & where) const
2306 // if cursor is not in the paragraph where the delete occured,
2308 if (cur.par() != where.par())
2311 // if cursor position is after the place where the delete occured,
2313 if (cur.pos() > where.pos())
2314 cur.pos(cur.pos()-1);
2316 // recompute row et al. for this cursor
2317 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2321 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2322 LyXCursor const & old_cursor) const
2324 // Would be wrong to delete anything if we have a selection.
2325 if (selection.set()) return false;
2327 // We allow all kinds of "mumbo-jumbo" when freespacing.
2328 if (textclasslist[bview->buffer()->params.textclass][
2329 old_cursor.par()->layout()].free_spacing
2330 || old_cursor.par()->isFreeSpacing())
2335 /* Ok I'll put some comments here about what is missing.
2336 I have fixed BackSpace (and thus Delete) to not delete
2337 double-spaces automagically. I have also changed Cut,
2338 Copy and Paste to hopefully do some sensible things.
2339 There are still some small problems that can lead to
2340 double spaces stored in the document file or space at
2341 the beginning of paragraphs. This happens if you have
2342 the cursor betwenn to spaces and then save. Or if you
2343 cut and paste and the selection have a space at the
2344 beginning and then save right after the paste. I am
2345 sure none of these are very hard to fix, but I will
2346 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2347 that I can get some feedback. (Lgb)
2350 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2351 // delete the LineSeparator.
2354 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2355 // delete the LineSeparator.
2358 // If the pos around the old_cursor were spaces, delete one of them.
2359 if (old_cursor.par() != cursor.par()
2360 || old_cursor.pos() != cursor.pos()) {
2361 // Only if the cursor has really moved
2363 if (old_cursor.pos() > 0
2364 && old_cursor.pos() < old_cursor.par()->size()
2365 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2366 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2367 old_cursor.par()->erase(old_cursor.pos() - 1);
2368 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2370 #ifdef WITH_WARNINGS
2371 #warning This will not work anymore when we have multiple views of the same buffer
2372 // In this case, we will have to correct also the cursors held by
2373 // other bufferviews. It will probably be easier to do that in a more
2374 // automated way in LyXCursor code. (JMarc 26/09/2001)
2376 // correct all cursors held by the LyXText
2377 fixCursorAfterDelete(bview, cursor, old_cursor);
2378 fixCursorAfterDelete(bview, selection.cursor,
2380 fixCursorAfterDelete(bview, selection.start,
2382 fixCursorAfterDelete(bview, selection.end, old_cursor);
2383 fixCursorAfterDelete(bview, last_sel_cursor,
2385 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2386 fixCursorAfterDelete(bview, toggle_end_cursor,
2392 // don't delete anything if this is the ONLY paragraph!
2393 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2396 // Do not delete empty paragraphs with keepempty set.
2398 [bview->buffer()->params.textclass]
2399 [old_cursor.par()->layout()].keepempty)
2402 // only do our magic if we changed paragraph
2403 if (old_cursor.par() == cursor.par())
2406 // record if we have deleted a paragraph
2407 // we can't possibly have deleted a paragraph before this point
2408 bool deleted = false;
2410 if ((old_cursor.par()->size() == 0
2411 || (old_cursor.par()->size() == 1
2412 && old_cursor.par()->isLineSeparator(0)))) {
2413 // ok, we will delete anything
2414 LyXCursor tmpcursor;
2416 // make sure that you do not delete any environments
2417 status(bview, LyXText::NEED_MORE_REFRESH);
2420 if (old_cursor.row()->previous()) {
2421 refresh_row = old_cursor.row()->previous();
2422 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2424 cursor = old_cursor; // that undo can restore the right cursor position
2425 Paragraph * endpar = old_cursor.par()->next();
2426 if (endpar && endpar->getDepth()) {
2427 while (endpar && endpar->getDepth()) {
2428 endpar = endpar->next();
2431 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2435 removeRow(old_cursor.row());
2436 if (ownerParagraph() == old_cursor.par()) {
2437 ownerParagraph(ownerParagraph()->next());
2440 delete old_cursor.par();
2442 /* Breakagain the next par. Needed because of
2443 * the parindent that can occur or dissappear.
2444 * The next row can change its height, if
2445 * there is another layout before */
2446 if (refresh_row->next()) {
2447 breakAgain(bview, refresh_row->next());
2448 updateCounters(bview, refresh_row);
2450 setHeightOfRow(bview, refresh_row);
2452 refresh_row = old_cursor.row()->next();
2453 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2456 cursor = old_cursor; // that undo can restore the right cursor position
2457 Paragraph * endpar = old_cursor.par()->next();
2458 if (endpar && endpar->getDepth()) {
2459 while (endpar && endpar->getDepth()) {
2460 endpar = endpar->next();
2463 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2467 removeRow(old_cursor.row());
2469 if (ownerParagraph() == old_cursor.par()) {
2470 ownerParagraph(ownerParagraph()->next());
2473 delete old_cursor.par();
2475 /* Breakagain the next par. Needed because of
2476 the parindent that can occur or dissappear.
2477 The next row can change its height, if
2478 there is another layout before */
2480 breakAgain(bview, refresh_row);
2481 updateCounters(bview, refresh_row->previous());
2486 setCursorIntern(bview, cursor.par(), cursor.pos());
2488 if (selection.cursor.par() == old_cursor.par()
2489 && selection.cursor.pos() == old_cursor.pos()) {
2490 // correct selection
2491 selection.cursor = cursor;
2495 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2496 redoParagraphs(bview, old_cursor,
2497 old_cursor.par()->next());
2499 setCursorIntern(bview, cursor.par(), cursor.pos());
2500 selection.cursor = cursor;
2507 void LyXText::toggleAppendix(BufferView * bview)
2509 Paragraph * par = cursor.par();
2510 bool start = !par->params().startOfAppendix();
2512 // ensure that we have only one start_of_appendix in this document
2513 Paragraph * tmp = ownerParagraph();
2514 for (; tmp; tmp = tmp->next()) {
2515 tmp->params().startOfAppendix(false);
2518 par->params().startOfAppendix(start);
2520 // we can set the refreshing parameters now
2521 status(bview, LyXText::NEED_MORE_REFRESH);
2523 refresh_row = 0; // not needed for full update
2524 updateCounters(bview, 0);
2525 setCursor(bview, cursor.par(), cursor.pos());
2529 Paragraph * LyXText::ownerParagraph() const
2532 return inset_owner->paragraph();
2534 return bv_owner->buffer()->paragraph;
2538 void LyXText::ownerParagraph(Paragraph * p) const
2541 inset_owner->paragraph(p);
2543 bv_owner->buffer()->paragraph = p;
2548 void LyXText::ownerParagraph(int id, Paragraph * p) const
2550 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2551 if (op && op->inInset()) {
2552 static_cast<InsetText *>(op->inInset())->paragraph(p);
2559 LyXText::text_status LyXText::status() const
2565 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2567 // well as much as I know && binds more then || so the above and the
2568 // below are identical (this for your known use of parentesis!)
2569 // Now some explanation:
2570 // We should only go up with refreshing code so this means that if
2571 // we have a MORE refresh we should never set it to LITTLE if we still
2572 // didn't handle it (and then it will be UNCHANGED. Now as long as
2573 // we stay inside one LyXText this may work but we need to tell the
2574 // outermost LyXText that it should REALLY draw us if there is some
2575 // change in a Inset::LyXText. So you see that when we are inside a
2576 // inset's LyXText we give the LITTLE to the outermost LyXText to
2577 // tell'em that it should redraw the actual row (where the inset
2578 // resides! Capito?!
2580 if ((status_ != NEED_MORE_REFRESH)
2581 || (status_ == NEED_MORE_REFRESH
2582 && st != NEED_VERY_LITTLE_REFRESH))
2585 if (inset_owner && st != UNCHANGED) {
2586 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2587 if (!bview->text->refresh_row) {
2588 bview->text->refresh_row = bview->text->cursor.row();
2589 bview->text->refresh_y = bview->text->cursor.y() -
2590 bview->text->cursor.row()->baseline();