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"
29 #include "frontends/Painter.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_y(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_y(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();
88 copylayouttype.erase();
89 number_of_rows = first_y = refresh_y = 0;
90 status_ = LyXText::UNCHANGED;
94 Paragraph * par = ownerParagraph();
95 current_font = getFont(bview->buffer(), par, 0);
98 insertParagraph(bview, par, lastrow);
101 setCursorIntern(bview, firstrow->par(), 0);
102 selection.cursor = cursor;
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next();
120 LyXFont const realizeFont(LyXFont const & font,
124 LyXFont tmpfont(font);
125 Paragraph::depth_type par_depth = par->getDepth();
127 // Resolve against environment font information
128 while (par && par_depth && !tmpfont.resolved()) {
129 par = par->outerHook();
131 #ifndef INHERIT_LANGUAGE
132 tmpfont.realize(textclasslist[buf->params.textclass][
133 par->layout()].font);
135 tmpfont.realize(textclasslist.
136 Style(buf->params.textclass,
138 buf->params.language);
140 par_depth = par->getDepth();
144 #ifndef INHERIT_LANGUAGE
145 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
147 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
148 buf->params.language);
157 // Gets the fully instantiated font at a given position in a paragraph
158 // Basically the same routine as Paragraph::getFont() in paragraph.C.
159 // The difference is that this one is used for displaying, and thus we
160 // are allowed to make cosmetic improvements. For instance make footnotes
162 // If position is -1, we get the layout font of the paragraph.
163 // If position is -2, we get the font of the manual label of the paragraph.
164 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
167 lyx::Assert(pos >= 0);
169 LyXLayout const & layout =
170 textclasslist[buf->params.textclass][par->layout()];
172 Paragraph::depth_type par_depth = par->getDepth();
173 // We specialize the 95% common case:
175 if (layout.labeltype == LABEL_MANUAL
176 && pos < beginningOfMainBody(buf, par)) {
178 LyXFont f = par->getFontSettings(buf->params, pos);
180 par->inInset()->getDrawFont(f);
181 #ifndef INHERIT_LANGUAGE
182 return f.realize(layout.reslabelfont);
184 return f.realize(layout.reslabelfont, buf->params.language);
187 LyXFont f = par->getFontSettings(buf->params, pos);
189 par->inInset()->getDrawFont(f);
190 #ifndef INHERIT_LANGUAGE
191 return f.realize(layout.resfont);
193 return f.realize(layout.resfont, buf->params.language);
198 // The uncommon case need not be optimized as much
202 if (pos < beginningOfMainBody(buf, par)) {
204 layoutfont = layout.labelfont;
207 layoutfont = layout.font;
210 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
211 #ifndef INHERIT_LANGUAGE
212 tmpfont.realize(layoutfont);
214 tmpfont.realize(layoutfont, buf->params.language);
217 par->inInset()->getDrawFont(tmpfont);
219 return realizeFont(tmpfont, buf, par);
223 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
225 LyXLayout const & layout =
226 textclasslist[buf->params.textclass][par->layout()];
228 Paragraph::depth_type par_depth = par->getDepth();
231 return layout.resfont;
234 return realizeFont(layout.font, buf, par);
238 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
240 LyXLayout const & layout =
241 textclasslist[buf->params.textclass][par->layout()];
243 Paragraph::depth_type par_depth = par->getDepth();
246 return layout.reslabelfont;
249 return realizeFont(layout.labelfont, buf, par);
253 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
254 pos_type pos, LyXFont const & fnt,
257 Buffer const * buf = bv->buffer();
258 LyXFont font = getFont(buf, par, pos);
259 font.update(fnt, buf->params.language, toggleall);
260 // Let the insets convert their font
261 if (par->isInset(pos)) {
262 Inset * inset = par->getInset(pos);
263 if (isEditableInset(inset)) {
264 UpdatableInset * uinset =
265 static_cast<UpdatableInset *>(inset);
266 uinset->setFont(bv, fnt, toggleall, true);
270 // Plug thru to version below:
271 setCharFont(buf, par, pos, font);
275 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
276 pos_type pos, LyXFont const & fnt)
280 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
281 LyXLayout const & layout = tclass[par->layout()];
283 // Get concrete layout font to reduce against
286 if (pos < beginningOfMainBody(buf, par))
287 layoutfont = layout.labelfont;
289 layoutfont = layout.font;
291 // Realize against environment font information
292 if (par->getDepth()) {
293 Paragraph * tp = par;
294 while (!layoutfont.resolved() && tp && tp->getDepth()) {
295 tp = tp->outerHook();
297 #ifndef INHERIT_LANGUAGE
298 layoutfont.realize(tclass[tp->layout()].font);
300 layoutfont.realize(textclasslist.
301 Style(buf->params.textclass,
303 buf->params.language);
308 #ifndef INHERIT_LANGUAGE
309 layoutfont.realize(tclass.defaultfont());
311 layoutfont.realize(tclass.defaultfont(), buf->params.language);
314 // Now, reduce font against full layout font
315 font.reduce(layoutfont);
317 par->setFont(pos, font);
321 // inserts a new row behind the specified row, increments
322 // the touched counters
323 void LyXText::insertRow(Row * row, Paragraph * par,
326 Row * tmprow = new Row;
329 tmprow->next(firstrow);
332 tmprow->previous(row);
333 tmprow->next(row->next());
338 tmprow->next()->previous(tmprow);
340 if (tmprow->previous())
341 tmprow->previous()->next(tmprow);
353 // removes the row and reset the touched counters
354 void LyXText::removeRow(Row * row) const
356 Row * row_prev = row->previous();
358 row->next()->previous(row_prev);
360 firstrow = row->next();
361 // lyx::Assert(firstrow);
363 row_prev->next(row->next());
365 if (row == lastrow) {
366 lyx::Assert(!row->next());
369 if (refresh_row == row) {
370 refresh_row = row_prev ? row_prev : row->next();
371 // what about refresh_y, refresh_height
374 height -= row->height(); // the text becomes smaller
377 --number_of_rows; // one row less
381 // remove all following rows of the paragraph of the specified row.
382 void LyXText::removeParagraph(Row * row) const
384 Paragraph * tmppar = row->par();
388 while (row && row->par() == tmppar) {
389 tmprow = row->next();
396 // insert the specified paragraph behind the specified row
397 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
400 insertRow(row, par, 0); /* insert a new row, starting
403 setCounter(bview->buffer(), par); // set the counters
405 // and now append the whole paragraph behind the new row
408 appendParagraph(bview, firstrow);
410 row->next()->height(0);
411 appendParagraph(bview, row->next());
416 Inset * LyXText::getInset() const
419 if (cursor.pos() == 0 && cursor.par()->bibkey) {
420 inset = cursor.par()->bibkey;
421 } else if (cursor.pos() < cursor.par()->size()
422 && cursor.par()->isInset(cursor.pos())) {
423 inset = cursor.par()->getInset(cursor.pos());
429 void LyXText::toggleInset(BufferView * bview)
431 Inset * inset = getInset();
432 // is there an editable inset at cursor position?
433 if (!isEditableInset(inset)) {
434 // No, try to see if we are inside a collapsable inset
435 if (inset_owner && inset_owner->owner()
436 && inset_owner->owner()->isOpen()) {
437 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
438 inset_owner->owner()->close(bview);
442 //bview->owner()->message(inset->editMessage());
444 // do we want to keep this?? (JMarc)
445 if (!isHighlyEditableInset(inset))
446 setCursorParUndo(bview);
448 if (inset->isOpen()) {
454 inset->open(bview, !inset->isOpen());
459 /* used in setlayout */
460 // Asger is not sure we want to do this...
461 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
464 LyXLayout const & layout =
465 textclasslist[buf->params.textclass][par->layout()];
468 for (pos_type pos = 0; pos < par->size(); ++pos) {
469 if (pos < beginningOfMainBody(buf, par))
470 layoutfont = layout.labelfont;
472 layoutfont = layout.font;
474 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
475 tmpfont.reduce(layoutfont);
476 par->setFont(pos, tmpfont);
481 Paragraph * LyXText::setLayout(BufferView * bview,
482 LyXCursor & cur, LyXCursor & sstart_cur,
483 LyXCursor & send_cur,
484 string const & layout)
486 Paragraph * endpar = send_cur.par()->next();
487 Paragraph * undoendpar = endpar;
489 if (endpar && endpar->getDepth()) {
490 while (endpar && endpar->getDepth()) {
491 endpar = endpar->next();
495 endpar = endpar->next(); // because of parindents etc.
498 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
500 // ok we have a selection. This is always between sstart_cur
501 // and sel_end cursor
503 Paragraph * par = sstart_cur.par();
504 Paragraph * epar = send_cur.par()->next();
506 LyXLayout const & lyxlayout =
507 textclasslist[bview->buffer()->params.textclass][layout];
510 par->applyLayout(layout);
511 makeFontEntriesLayoutSpecific(bview->buffer(), par);
512 Paragraph * fppar = par;
513 fppar->params().spaceTop(lyxlayout.fill_top ?
514 VSpace(VSpace::VFILL)
515 : VSpace(VSpace::NONE));
516 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
517 VSpace(VSpace::VFILL)
518 : VSpace(VSpace::NONE));
519 if (lyxlayout.margintype == MARGIN_MANUAL)
520 par->setLabelWidthString(lyxlayout.labelstring());
521 if (lyxlayout.labeltype != LABEL_BIBLIO
523 delete fppar->bibkey;
528 } while (par != epar);
534 // set layout over selection and make a total rebreak of those paragraphs
535 void LyXText::setLayout(BufferView * bview, string const & layout)
537 LyXCursor tmpcursor = cursor; /* store the current cursor */
539 // if there is no selection just set the layout
540 // of the current paragraph */
541 if (!selection.set()) {
542 selection.start = cursor; // dummy selection
543 selection.end = cursor;
545 Paragraph * endpar = setLayout(bview, cursor, selection.start,
546 selection.end, layout);
547 redoParagraphs(bview, selection.start, endpar);
549 // we have to reset the selection, because the
550 // geometry could have changed
551 setCursor(bview, selection.start.par(),
552 selection.start.pos(), false);
553 selection.cursor = cursor;
554 setCursor(bview, selection.end.par(), selection.end.pos(), false);
555 updateCounters(bview, cursor.row());
558 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
562 // increment depth over selection and
563 // make a total rebreak of those paragraphs
564 void LyXText::incDepth(BufferView * bview)
566 // If there is no selection, just use the current paragraph
567 if (!selection.set()) {
568 selection.start = cursor; // dummy selection
569 selection.end = cursor;
572 // We end at the next paragraph with depth 0
573 Paragraph * endpar = selection.end.par()->next();
575 Paragraph * undoendpar = endpar;
577 if (endpar && endpar->getDepth()) {
578 while (endpar && endpar->getDepth()) {
579 endpar = endpar->next();
583 endpar = endpar->next(); // because of parindents etc.
586 setUndo(bview, Undo::EDIT,
587 selection.start.par(), undoendpar);
589 LyXCursor tmpcursor = cursor; // store the current cursor
591 // ok we have a selection. This is always between sel_start_cursor
592 // and sel_end cursor
593 cursor = selection.start;
595 bool anything_changed = false;
597 LyXTextClass const & tclass =
598 textclasslist[bview->buffer()->params.textclass];
601 // NOTE: you can't change the depth of a bibliography entry
602 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
603 Paragraph * prev = cursor.par()->previous();
606 if (cursor.par()->getDepth()
607 < prev->getMaxDepthAfter(bview->buffer())){
608 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
609 anything_changed = true;
613 if (cursor.par() == selection.end.par())
615 cursor.par(cursor.par()->next());
618 // if nothing changed set all depth to 0
619 if (!anything_changed) {
620 cursor = selection.start;
621 while (cursor.par() != selection.end.par()) {
622 cursor.par()->params().depth(0);
623 cursor.par(cursor.par()->next());
625 cursor.par()->params().depth(0);
628 redoParagraphs(bview, selection.start, endpar);
630 // we have to reset the selection, because the
631 // geometry could have changed
632 setCursor(bview, selection.start.par(), selection.start.pos());
633 selection.cursor = cursor;
634 setCursor(bview, selection.end.par(), selection.end.pos());
635 updateCounters(bview, cursor.row());
638 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
642 // decrement depth over selection and
643 // make a total rebreak of those paragraphs
644 void LyXText::decDepth(BufferView * bview)
646 // if there is no selection just set the layout
647 // of the current paragraph
648 if (!selection.set()) {
649 selection.start = cursor; // dummy selection
650 selection.end = cursor;
652 Paragraph * endpar = selection.end.par()->next();
653 Paragraph * undoendpar = endpar;
655 if (endpar && endpar->getDepth()) {
656 while (endpar && endpar->getDepth()) {
657 endpar = endpar->next();
661 endpar = endpar->next(); // because of parindents etc.
664 setUndo(bview, Undo::EDIT,
665 selection.start.par(), undoendpar);
667 LyXCursor tmpcursor = cursor; // store the current cursor
669 // ok we have a selection. This is always between sel_start_cursor
670 // and sel_end cursor
671 cursor = selection.start;
674 if (cursor.par()->params().depth()) {
675 cursor.par()->params()
676 .depth(cursor.par()->params().depth() - 1);
678 if (cursor.par() == selection.end.par()) {
681 cursor.par(cursor.par()->next());
684 redoParagraphs(bview, selection.start, endpar);
686 // we have to reset the selection, because the
687 // geometry could have changed
688 setCursor(bview, selection.start.par(),
689 selection.start.pos());
690 selection.cursor = cursor;
691 setCursor(bview, selection.end.par(), selection.end.pos());
692 updateCounters(bview, cursor.row());
695 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
699 // set font over selection and make a total rebreak of those paragraphs
700 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
702 // if there is no selection just set the current_font
703 if (!selection.set()) {
704 // Determine basis font
706 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
708 layoutfont = getLabelFont(bview->buffer(),
711 layoutfont = getLayoutFont(bview->buffer(),
714 // Update current font
715 real_current_font.update(font,
716 bview->buffer()->params.language,
719 // Reduce to implicit settings
720 current_font = real_current_font;
721 current_font.reduce(layoutfont);
722 // And resolve it completely
723 #ifndef INHERIT_LANGUAGE
724 real_current_font.realize(layoutfont);
726 real_current_font.realize(layoutfont,
727 bview->buffer()->params.language);
732 LyXCursor tmpcursor = cursor; // store the current cursor
734 // ok we have a selection. This is always between sel_start_cursor
735 // and sel_end cursor
737 setUndo(bview, Undo::EDIT,
738 selection.start.par(), selection.end.par()->next());
740 cursor = selection.start;
741 while (cursor.par() != selection.end.par() ||
742 cursor.pos() < selection.end.pos())
744 if (cursor.pos() < cursor.par()->size()) {
745 // an open footnote should behave like a closed one
746 setCharFont(bview, cursor.par(), cursor.pos(),
748 cursor.pos(cursor.pos() + 1);
751 cursor.par(cursor.par()->next());
756 redoParagraphs(bview, selection.start, selection.end.par()->next());
758 // we have to reset the selection, because the
759 // geometry could have changed, but we keep
760 // it for user convenience
761 setCursor(bview, selection.start.par(), selection.start.pos());
762 selection.cursor = cursor;
763 setCursor(bview, selection.end.par(), selection.end.pos());
765 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
766 tmpcursor.boundary());
770 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
772 Row * tmprow = cur.row();
773 int y = cur.y() - tmprow->baseline();
775 setHeightOfRow(bview, tmprow);
777 while (tmprow->previous()
778 && tmprow->previous()->par() == tmprow->par()) {
779 tmprow = tmprow->previous();
780 y -= tmprow->height();
781 setHeightOfRow(bview, tmprow);
784 // we can set the refreshing parameters now
785 status(bview, LyXText::NEED_MORE_REFRESH);
787 refresh_row = tmprow;
788 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
792 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
794 Row * tmprow = cur.row();
796 int y = cur.y() - tmprow->baseline();
797 setHeightOfRow(bview, tmprow);
799 while (tmprow->previous()
800 && tmprow->previous()->par() == tmprow->par()) {
801 tmprow = tmprow->previous();
802 y -= tmprow->height();
805 // we can set the refreshing parameters now
806 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
808 refresh_row = tmprow;
810 status(bview, LyXText::NEED_MORE_REFRESH);
811 setCursor(bview, cur.par(), cur.pos());
815 // deletes and inserts again all paragaphs between the cursor
816 // and the specified par
817 // This function is needed after SetLayout and SetFont etc.
818 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
819 Paragraph const * endpar) const
822 Paragraph * tmppar = 0;
823 Paragraph * first_phys_par = 0;
825 Row * tmprow = cur.row();
827 int y = cur.y() - tmprow->baseline();
829 if (!tmprow->previous()) {
830 // a trick/hack for UNDO
831 // This is needed because in an UNDO/REDO we could have changed
832 // the ownerParagrah() so the paragraph inside the row is NOT
833 // my really first par anymore. Got it Lars ;) (Jug 20011206)
834 first_phys_par = ownerParagraph();
836 first_phys_par = tmprow->par();
837 while (tmprow->previous()
838 && tmprow->previous()->par() == first_phys_par)
840 tmprow = tmprow->previous();
841 y -= tmprow->height();
845 // we can set the refreshing parameters now
846 status(bview, LyXText::NEED_MORE_REFRESH);
848 refresh_row = tmprow->previous(); /* the real refresh row will
849 be deleted, so I store
853 tmppar = tmprow->next()->par();
856 while (tmprow->next() && tmppar != endpar) {
857 removeRow(tmprow->next());
858 if (tmprow->next()) {
859 tmppar = tmprow->next()->par();
865 // remove the first one
866 tmprow2 = tmprow; /* this is because tmprow->previous()
868 tmprow = tmprow->previous();
871 tmppar = first_phys_par;
875 insertParagraph(bview, tmppar, tmprow);
879 while (tmprow->next()
880 && tmprow->next()->par() == tmppar) {
881 tmprow = tmprow->next();
883 tmppar = tmppar->next();
885 } while (tmppar && tmppar != endpar);
887 // this is because of layout changes
889 refresh_y -= refresh_row->height();
890 setHeightOfRow(bview, refresh_row);
892 refresh_row = firstrow;
894 setHeightOfRow(bview, refresh_row);
897 if (tmprow && tmprow->next())
898 setHeightOfRow(bview, tmprow->next());
902 void LyXText::fullRebreak(BufferView * bview)
908 if (need_break_row) {
909 breakAgain(bview, need_break_row);
916 // important for the screen
919 /* the cursor set functions have a special mechanism. When they
920 * realize, that you left an empty paragraph, they will delete it.
921 * They also delete the corresponding row */
923 // need the selection cursor:
924 void LyXText::setSelection(BufferView * bview)
926 bool const lsel = selection.set();
928 if (!selection.set()) {
929 last_sel_cursor = selection.cursor;
930 selection.start = selection.cursor;
931 selection.end = selection.cursor;
936 // first the toggling area
937 if (cursor.y() < last_sel_cursor.y()
938 || (cursor.y() == last_sel_cursor.y()
939 && cursor.x() < last_sel_cursor.x())) {
940 toggle_end_cursor = last_sel_cursor;
941 toggle_cursor = cursor;
943 toggle_end_cursor = cursor;
944 toggle_cursor = last_sel_cursor;
947 last_sel_cursor = cursor;
949 // and now the whole selection
951 if (selection.cursor.par() == cursor.par())
952 if (selection.cursor.pos() < cursor.pos()) {
953 selection.end = cursor;
954 selection.start = selection.cursor;
956 selection.end = selection.cursor;
957 selection.start = cursor;
959 else if (selection.cursor.y() < cursor.y() ||
960 (selection.cursor.y() == cursor.y()
961 && selection.cursor.x() < cursor.x())) {
962 selection.end = cursor;
963 selection.start = selection.cursor;
966 selection.end = selection.cursor;
967 selection.start = cursor;
970 // a selection with no contents is not a selection
971 if (selection.start.par() == selection.end.par() &&
972 selection.start.pos() == selection.end.pos())
973 selection.set(false);
975 if (inset_owner && (selection.set() || lsel))
976 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
980 string const LyXText::selectionAsString(Buffer const * buffer,
983 if (!selection.set()) return string();
986 // Special handling if the whole selection is within one paragraph
987 if (selection.start.par() == selection.end.par()) {
988 result += selection.start.par()->asString(buffer,
989 selection.start.pos(),
995 // The selection spans more than one paragraph
997 // First paragraph in selection
998 result += selection.start.par()->asString(buffer,
999 selection.start.pos(),
1000 selection.start.par()->size(),
1004 // The paragraphs in between (if any)
1005 LyXCursor tmpcur(selection.start);
1006 tmpcur.par(tmpcur.par()->next());
1007 while (tmpcur.par() != selection.end.par()) {
1008 result += tmpcur.par()->asString(buffer, 0,
1009 tmpcur.par()->size(),
1011 tmpcur.par(tmpcur.par()->next());
1014 // Last paragraph in selection
1015 result += selection.end.par()->asString(buffer, 0,
1016 selection.end.pos(), label);
1022 void LyXText::clearSelection() const
1024 selection.set(false);
1025 selection.mark(false);
1026 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1027 // reset this in the bv_owner!
1028 if (bv_owner && bv_owner->text)
1029 bv_owner->text->xsel_cache.set(false);
1033 void LyXText::cursorHome(BufferView * bview) const
1035 setCursor(bview, cursor.par(), cursor.row()->pos());
1039 void LyXText::cursorEnd(BufferView * bview) const
1041 if (!cursor.row()->next()
1042 || cursor.row()->next()->par() != cursor.row()->par()) {
1043 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1045 if (cursor.par()->size() &&
1046 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1047 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1048 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1050 setCursor(bview,cursor.par(),
1051 rowLast(cursor.row()) + 1);
1057 void LyXText::cursorTop(BufferView * bview) const
1059 while (cursor.par()->previous())
1060 cursor.par(cursor.par()->previous());
1061 setCursor(bview, cursor.par(), 0);
1065 void LyXText::cursorBottom(BufferView * bview) const
1067 while (cursor.par()->next())
1068 cursor.par(cursor.par()->next());
1069 setCursor(bview, cursor.par(), cursor.par()->size());
1073 void LyXText::toggleFree(BufferView * bview,
1074 LyXFont const & font, bool toggleall)
1076 // If the mask is completely neutral, tell user
1077 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1078 // Could only happen with user style
1079 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1083 // Try implicit word selection
1084 // If there is a change in the language the implicit word selection
1086 LyXCursor resetCursor = cursor;
1087 bool implicitSelection = (font.language() == ignore_language
1088 && font.number() == LyXFont::IGNORE)
1089 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1092 setFont(bview, font, toggleall);
1094 // Implicit selections are cleared afterwards
1095 //and cursor is set to the original position.
1096 if (implicitSelection) {
1098 cursor = resetCursor;
1099 setCursor(bview, cursor.par(), cursor.pos());
1100 selection.cursor = cursor;
1103 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1107 string LyXText::getStringToIndex(BufferView * bview)
1111 // Try implicit word selection
1112 // If there is a change in the language the implicit word selection
1114 LyXCursor resetCursor = cursor;
1115 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1117 if (!selection.set()) {
1118 bview->owner()->message(_("Nothing to index!"));
1121 if (selection.start.par() != selection.end.par()) {
1122 bview->owner()->message(_("Cannot index more than one paragraph!"));
1126 idxstring = selectionAsString(bview->buffer(), false);
1128 // Implicit selections are cleared afterwards
1129 //and cursor is set to the original position.
1130 if (implicitSelection) {
1132 cursor = resetCursor;
1133 setCursor(bview, cursor.par(), cursor.pos());
1134 selection.cursor = cursor;
1140 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1141 Paragraph const * par) const
1143 if (textclasslist[buf->params.textclass][
1144 par->layout()].labeltype != LABEL_MANUAL)
1147 return par->beginningOfMainBody();
1151 /* the DTP switches for paragraphs. LyX will store them in the
1152 * first physicla paragraph. When a paragraph is broken, the top settings
1153 * rest, the bottom settings are given to the new one. So I can make shure,
1154 * they do not duplicate themself and you cannnot make dirty things with
1157 void LyXText::setParagraph(BufferView * bview,
1158 bool line_top, bool line_bottom,
1159 bool pagebreak_top, bool pagebreak_bottom,
1160 VSpace const & space_top,
1161 VSpace const & space_bottom,
1162 Spacing const & spacing,
1164 string labelwidthstring,
1167 LyXCursor tmpcursor = cursor;
1168 if (!selection.set()) {
1169 selection.start = cursor;
1170 selection.end = cursor;
1173 // make sure that the depth behind the selection are restored, too
1174 Paragraph * endpar = selection.end.par()->next();
1175 Paragraph * undoendpar = endpar;
1177 if (endpar && endpar->getDepth()) {
1178 while (endpar && endpar->getDepth()) {
1179 endpar = endpar->next();
1180 undoendpar = endpar;
1184 // because of parindents etc.
1185 endpar = endpar->next();
1188 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1191 Paragraph * tmppar = selection.end.par();
1192 LyXTextClass const & tclass =
1193 textclasslist[bview->buffer()->params.textclass];
1195 while (tmppar != selection.start.par()->previous()) {
1196 setCursor(bview, tmppar, 0);
1197 status(bview, LyXText::NEED_MORE_REFRESH);
1198 refresh_row = cursor.row();
1199 refresh_y = cursor.y() - cursor.row()->baseline();
1200 cursor.par()->params().lineTop(line_top);
1201 cursor.par()->params().lineBottom(line_bottom);
1202 cursor.par()->params().pagebreakTop(pagebreak_top);
1203 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1204 cursor.par()->params().spaceTop(space_top);
1205 cursor.par()->params().spaceBottom(space_bottom);
1206 cursor.par()->params().spacing(spacing);
1207 // does the layout allow the new alignment?
1208 LyXLayout const & layout = tclass[cursor.par()->layout()];
1210 if (align == LYX_ALIGN_LAYOUT)
1211 align = layout.align;
1212 if (align & layout.alignpossible) {
1213 if (align == layout.align)
1214 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1216 cursor.par()->params().align(align);
1218 cursor.par()->setLabelWidthString(labelwidthstring);
1219 cursor.par()->params().noindent(noindent);
1220 tmppar = cursor.par()->previous();
1223 redoParagraphs(bview, selection.start, endpar);
1226 setCursor(bview, selection.start.par(), selection.start.pos());
1227 selection.cursor = cursor;
1228 setCursor(bview, selection.end.par(), selection.end.pos());
1229 setSelection(bview);
1230 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1232 bview->updateInset(inset_owner, true);
1236 char loweralphaCounter(int n)
1238 if (n < 1 || n > 26)
1248 char alphaCounter(int n)
1250 if (n < 1 || n > 26)
1258 char hebrewCounter(int n)
1260 static const char hebrew[22] = {
1261 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1262 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1263 '÷', 'ø', 'ù', 'ú'
1265 if (n < 1 || n > 22)
1273 string const romanCounter(int n)
1275 static char const * roman[20] = {
1276 "i", "ii", "iii", "iv", "v",
1277 "vi", "vii", "viii", "ix", "x",
1278 "xi", "xii", "xiii", "xiv", "xv",
1279 "xvi", "xvii", "xviii", "xix", "xx"
1281 if (n < 1 || n > 20)
1290 // set the counter of a paragraph. This includes the labels
1291 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1293 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1294 LyXLayout const & layout = textclass[par->layout()];
1297 // copy the prev-counters to this one,
1298 // unless this is the first paragraph
1299 if (par->previous()) {
1300 for (int i = 0; i < 10; ++i) {
1301 par->setCounter(i, par->previous()->getFirstCounter(i));
1303 par->params().appendix(par->previous()->params().appendix());
1304 if (!par->params().appendix() && par->params().startOfAppendix()) {
1305 par->params().appendix(true);
1306 for (int i = 0; i < 10; ++i) {
1307 par->setCounter(i, 0);
1310 par->enumdepth = par->previous()->enumdepth;
1311 par->itemdepth = par->previous()->itemdepth;
1313 for (int i = 0; i < 10; ++i) {
1314 par->setCounter(i, 0);
1316 par->params().appendix(par->params().startOfAppendix());
1321 /* Maybe we have to increment the enumeration depth.
1322 * BUT, enumeration in a footnote is considered in isolation from its
1323 * surrounding paragraph so don't increment if this is the
1324 * first line of the footnote
1325 * AND, bibliographies can't have their depth changed ie. they
1326 * are always of depth 0
1329 && par->previous()->getDepth() < par->getDepth()
1330 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1331 && par->enumdepth < 3
1332 && layout.labeltype != LABEL_BIBLIO) {
1336 // Maybe we have to decrement the enumeration depth, see note above
1338 && par->previous()->getDepth() > par->getDepth()
1339 && layout.labeltype != LABEL_BIBLIO) {
1340 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1341 par->setCounter(6 + par->enumdepth,
1342 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1343 /* reset the counters.
1344 * A depth change is like a breaking layout
1346 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1347 par->setCounter(i, 0);
1350 if (!par->params().labelString().empty()) {
1351 par->params().labelString(string());
1354 if (layout.margintype == MARGIN_MANUAL) {
1355 if (par->params().labelWidthString().empty()) {
1356 par->setLabelWidthString(layout.labelstring());
1359 par->setLabelWidthString(string());
1362 // is it a layout that has an automatic label?
1363 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1365 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1366 if (i >= 0 && i<= buf->params.secnumdepth) {
1367 par->incCounter(i); // increment the counter
1369 // Is there a label? Useful for Chapter layout
1370 if (!par->params().appendix()) {
1371 if (!layout.labelstring().empty())
1372 par->params().labelString(layout.labelstring());
1374 par->params().labelString(string());
1376 if (!layout.labelstring_appendix().empty())
1377 par->params().labelString(layout.labelstring_appendix());
1379 par->params().labelString(string());
1384 if (!par->params().appendix()) {
1385 switch (2 * LABEL_COUNTER_CHAPTER -
1386 textclass.maxcounter() + i) {
1387 case LABEL_COUNTER_CHAPTER:
1388 s << par->getCounter(i);
1390 case LABEL_COUNTER_SECTION:
1391 s << par->getCounter(i - 1) << '.'
1392 << par->getCounter(i);
1394 case LABEL_COUNTER_SUBSECTION:
1395 s << par->getCounter(i - 2) << '.'
1396 << par->getCounter(i - 1) << '.'
1397 << par->getCounter(i);
1399 case LABEL_COUNTER_SUBSUBSECTION:
1400 s << par->getCounter(i - 3) << '.'
1401 << par->getCounter(i - 2) << '.'
1402 << par->getCounter(i - 1) << '.'
1403 << par->getCounter(i);
1406 case LABEL_COUNTER_PARAGRAPH:
1407 s << par->getCounter(i - 4) << '.'
1408 << par->getCounter(i - 3) << '.'
1409 << par->getCounter(i - 2) << '.'
1410 << par->getCounter(i - 1) << '.'
1411 << par->getCounter(i);
1413 case LABEL_COUNTER_SUBPARAGRAPH:
1414 s << par->getCounter(i - 5) << '.'
1415 << par->getCounter(i - 4) << '.'
1416 << par->getCounter(i - 3) << '.'
1417 << par->getCounter(i - 2) << '.'
1418 << par->getCounter(i - 1) << '.'
1419 << par->getCounter(i);
1423 // Can this ever be reached? And in the
1424 // case it is, how can this be correct?
1426 s << par->getCounter(i) << '.';
1429 } else { // appendix
1430 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1431 case LABEL_COUNTER_CHAPTER:
1432 if (par->isRightToLeftPar(buf->params))
1433 s << hebrewCounter(par->getCounter(i));
1435 s << alphaCounter(par->getCounter(i));
1437 case LABEL_COUNTER_SECTION:
1438 if (par->isRightToLeftPar(buf->params))
1439 s << hebrewCounter(par->getCounter(i - 1));
1441 s << alphaCounter(par->getCounter(i - 1));
1444 << par->getCounter(i);
1447 case LABEL_COUNTER_SUBSECTION:
1448 if (par->isRightToLeftPar(buf->params))
1449 s << hebrewCounter(par->getCounter(i - 2));
1451 s << alphaCounter(par->getCounter(i - 2));
1454 << par->getCounter(i-1) << '.'
1455 << par->getCounter(i);
1458 case LABEL_COUNTER_SUBSUBSECTION:
1459 if (par->isRightToLeftPar(buf->params))
1460 s << hebrewCounter(par->getCounter(i-3));
1462 s << alphaCounter(par->getCounter(i-3));
1465 << par->getCounter(i-2) << '.'
1466 << par->getCounter(i-1) << '.'
1467 << par->getCounter(i);
1470 case LABEL_COUNTER_PARAGRAPH:
1471 if (par->isRightToLeftPar(buf->params))
1472 s << hebrewCounter(par->getCounter(i-4));
1474 s << alphaCounter(par->getCounter(i-4));
1477 << par->getCounter(i-3) << '.'
1478 << par->getCounter(i-2) << '.'
1479 << par->getCounter(i-1) << '.'
1480 << par->getCounter(i);
1483 case LABEL_COUNTER_SUBPARAGRAPH:
1484 if (par->isRightToLeftPar(buf->params))
1485 s << hebrewCounter(par->getCounter(i-5));
1487 s << alphaCounter(par->getCounter(i-5));
1490 << par->getCounter(i-4) << '.'
1491 << par->getCounter(i-3) << '.'
1492 << par->getCounter(i-2) << '.'
1493 << par->getCounter(i-1) << '.'
1494 << par->getCounter(i);
1498 // Can this ever be reached? And in the
1499 // case it is, how can this be correct?
1501 s << par->getCounter(i) << '.';
1507 par->params().labelString(par->params().labelString() +s.str().c_str());
1508 // We really want to remove the c_str as soon as
1511 for (i++; i < 10; ++i) {
1512 // reset the following counters
1513 par->setCounter(i, 0);
1515 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1516 for (i++; i < 10; ++i) {
1517 // reset the following counters
1518 par->setCounter(i, 0);
1520 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1521 par->incCounter(i + par->enumdepth);
1522 int number = par->getCounter(i + par->enumdepth);
1526 switch (par->enumdepth) {
1528 if (par->isRightToLeftPar(buf->params))
1530 << hebrewCounter(number)
1534 << loweralphaCounter(number)
1538 if (par->isRightToLeftPar(buf->params))
1539 s << '.' << romanCounter(number);
1541 s << romanCounter(number) << '.';
1544 if (par->isRightToLeftPar(buf->params))
1546 << alphaCounter(number);
1548 s << alphaCounter(number)
1552 if (par->isRightToLeftPar(buf->params))
1559 par->params().labelString(s.str().c_str());
1561 for (i += par->enumdepth + 1; i < 10; ++i) {
1562 // reset the following counters
1563 par->setCounter(i, 0);
1567 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1568 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1570 int number = par->getCounter(i);
1572 InsetCommandParams p("bibitem" );
1573 par->bibkey = new InsetBibKey(p);
1575 par->bibkey->setCounter(number);
1576 par->params().labelString(layout.labelstring());
1578 // In biblio should't be following counters but...
1580 string s = layout.labelstring();
1582 // the caption hack:
1583 if (layout.labeltype == LABEL_SENSITIVE) {
1584 bool isOK (par->inInset() && par->inInset()->owner() &&
1585 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1588 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1590 = floatList.getType(tmp->type());
1591 // We should get the correct number here too.
1592 s = fl.name() + " #:";
1594 /* par->SetLayout(0);
1595 s = layout->labelstring; */
1596 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1597 ? " :úåòîùî øñç" : "Senseless: ";
1600 par->params().labelString(s);
1602 /* reset the enumeration counter. They are always resetted
1603 * when there is any other layout between */
1604 for (int i = 6 + par->enumdepth; i < 10; ++i)
1605 par->setCounter(i, 0);
1610 // Updates all counters BEHIND the row. Changed paragraphs
1611 // with a dynamic left margin will be rebroken.
1612 void LyXText::updateCounters(BufferView * bview, Row * row) const
1620 par = row->par()->next();
1624 while (row->par() != par)
1627 setCounter(bview->buffer(), par);
1629 // now check for the headline layouts. remember that they
1630 // have a dynamic left margin
1631 LyXTextClass const & tclass =
1632 textclasslist[bview->buffer()->params.textclass];
1633 LyXLayout const & layout = tclass[par->layout()];
1635 if (layout.margintype == MARGIN_DYNAMIC
1636 || layout.labeltype == LABEL_SENSITIVE) {
1637 // Rebreak the paragraph
1638 removeParagraph(row);
1639 appendParagraph(bview, row);
1646 void LyXText::insertInset(BufferView * bview, Inset * inset)
1648 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1650 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1652 cursor.par()->insertInset(cursor.pos(), inset);
1653 // Just to rebreak and refresh correctly.
1654 // The character will not be inserted a second time
1655 insertChar(bview, Paragraph::META_INSET);
1656 // If we enter a highly editable inset the cursor should be to before
1657 // the inset. This couldn't happen before as Undo was not handled inside
1658 // inset now after the Undo LyX tries to call inset->Edit(...) again
1659 // and cannot do this as the cursor is behind the inset and GetInset
1660 // does not return the inset!
1661 if (isHighlyEditableInset(inset)) {
1662 cursorLeft(bview, true);
1668 void LyXText::copyEnvironmentType()
1670 copylayouttype = cursor.par()->layout();
1674 void LyXText::pasteEnvironmentType(BufferView * bview)
1676 setLayout(bview, copylayouttype);
1680 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1682 // Stuff what we got on the clipboard. Even if there is no selection.
1684 // There is a problem with having the stuffing here in that the
1685 // larger the selection the slower LyX will get. This can be
1686 // solved by running the line below only when the selection has
1687 // finished. The solution used currently just works, to make it
1688 // faster we need to be more clever and probably also have more
1689 // calls to stuffClipboard. (Lgb)
1690 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1692 // This doesn't make sense, if there is no selection
1693 if (!selection.set())
1696 // OK, we have a selection. This is always between selection.start
1697 // and selection.end
1699 // make sure that the depth behind the selection are restored, too
1700 Paragraph * endpar = selection.end.par()->next();
1701 Paragraph * undoendpar = endpar;
1703 if (endpar && endpar->getDepth()) {
1704 while (endpar && endpar->getDepth()) {
1705 endpar = endpar->next();
1706 undoendpar = endpar;
1708 } else if (endpar) {
1709 endpar = endpar->next(); // because of parindents etc.
1712 setUndo(bview, Undo::DELETE,
1713 selection.start.par(), undoendpar);
1715 // there are two cases: cut only within one paragraph or
1716 // more than one paragraph
1717 if (selection.start.par() == selection.end.par()) {
1718 // only within one paragraph
1719 endpar = selection.end.par();
1720 int pos = selection.end.pos();
1721 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1722 selection.start.pos(), pos,
1723 bview->buffer()->params.textclass,
1725 selection.end.pos(pos);
1727 endpar = selection.end.par();
1728 int pos = selection.end.pos();
1729 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1730 selection.start.pos(), pos,
1731 bview->buffer()->params.textclass,
1734 selection.end.par(endpar);
1735 selection.end.pos(pos);
1736 cursor.pos(selection.end.pos());
1738 endpar = endpar->next();
1740 // sometimes necessary
1742 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1744 redoParagraphs(bview, selection.start, endpar);
1746 // cutSelection can invalidate the cursor so we need to set
1748 cursor = selection.start;
1750 // need a valid cursor. (Lgb)
1753 setCursor(bview, cursor.par(), cursor.pos());
1754 selection.cursor = cursor;
1755 updateCounters(bview, cursor.row());
1759 void LyXText::copySelection(BufferView * bview)
1761 // stuff the selection onto the X clipboard, from an explicit copy request
1762 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1764 // this doesnt make sense, if there is no selection
1765 if (!selection.set())
1768 // ok we have a selection. This is always between selection.start
1769 // and sel_end cursor
1771 // copy behind a space if there is one
1772 while (selection.start.par()->size() > selection.start.pos()
1773 && selection.start.par()->isLineSeparator(selection.start.pos())
1774 && (selection.start.par() != selection.end.par()
1775 || selection.start.pos() < selection.end.pos()))
1776 selection.start.pos(selection.start.pos() + 1);
1778 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1779 selection.start.pos(), selection.end.pos(),
1780 bview->buffer()->params.textclass);
1784 void LyXText::pasteSelection(BufferView * bview)
1786 // this does not make sense, if there is nothing to paste
1787 if (!CutAndPaste::checkPastePossible(cursor.par()))
1790 setUndo(bview, Undo::INSERT,
1791 cursor.par(), cursor.par()->next());
1794 Paragraph * actpar = cursor.par();
1795 int pos = cursor.pos();
1797 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1798 bview->buffer()->params.textclass);
1800 redoParagraphs(bview, cursor, endpar);
1802 setCursor(bview, cursor.par(), cursor.pos());
1805 selection.cursor = cursor;
1806 setCursor(bview, actpar, pos);
1807 setSelection(bview);
1808 updateCounters(bview, cursor.row());
1812 // sets the selection over the number of characters of string, no check!!
1813 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1818 selection.cursor = cursor;
1819 for (string::size_type i = 0; i < str.length(); ++i)
1821 setSelection(bview);
1825 // simple replacing. The font of the first selected character is used
1826 void LyXText::replaceSelectionWithString(BufferView * bview,
1829 setCursorParUndo(bview);
1832 if (!selection.set()) { // create a dummy selection
1833 selection.end = cursor;
1834 selection.start = cursor;
1837 // Get font setting before we cut
1838 pos_type pos = selection.end.pos();
1839 LyXFont const font = selection.start.par()
1840 ->getFontSettings(bview->buffer()->params,
1841 selection.start.pos());
1843 // Insert the new string
1844 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1845 selection.end.par()->insertChar(pos, (*cit), font);
1849 // Cut the selection
1850 cutSelection(bview, true, false);
1856 // needed to insert the selection
1857 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1859 Paragraph * par = cursor.par();
1860 pos_type pos = cursor.pos();
1861 Paragraph * endpar = cursor.par()->next();
1863 setCursorParUndo(bview);
1865 // only to be sure, should not be neccessary
1868 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1870 redoParagraphs(bview, cursor, endpar);
1871 setCursor(bview, cursor.par(), cursor.pos());
1872 selection.cursor = cursor;
1873 setCursor(bview, par, pos);
1874 setSelection(bview);
1878 // turns double-CR to single CR, others where converted into one
1879 // blank. Then InsertStringAsLines is called
1880 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1882 string linestr(str);
1883 bool newline_inserted = false;
1884 for (string::size_type i = 0; i < linestr.length(); ++i) {
1885 if (linestr[i] == '\n') {
1886 if (newline_inserted) {
1887 // we know that \r will be ignored by
1888 // InsertStringA. Of course, it is a dirty
1889 // trick, but it works...
1890 linestr[i - 1] = '\r';
1894 newline_inserted = true;
1896 } else if (IsPrintable(linestr[i])) {
1897 newline_inserted = false;
1900 insertStringAsLines(bview, linestr);
1904 bool LyXText::gotoNextInset(BufferView * bview,
1905 vector<Inset::Code> const & codes,
1906 string const & contents) const
1908 LyXCursor res = cursor;
1911 if (res.pos() < res.par()->size() - 1) {
1912 res.pos(res.pos() + 1);
1914 res.par(res.par()->next());
1918 } while (res.par() &&
1919 !(res.par()->isInset(res.pos())
1920 && (inset = res.par()->getInset(res.pos())) != 0
1921 && find(codes.begin(), codes.end(), inset->lyxCode())
1923 && (contents.empty() ||
1924 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1928 setCursor(bview, res.par(), res.pos(), false);
1935 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1938 LyXCursor tmpcursor;
1942 Row * row = getRow(par, pos, y);
1944 // is there a break one row above
1945 if (row->previous() && row->previous()->par() == row->par()) {
1946 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1947 if (z >= row->pos()) {
1948 // set the dimensions of the row above
1949 y -= row->previous()->height();
1951 refresh_row = row->previous();
1952 status(bview, LyXText::NEED_MORE_REFRESH);
1954 breakAgain(bview, row->previous());
1956 // set the cursor again. Otherwise
1957 // dangling pointers are possible
1958 setCursor(bview, cursor.par(), cursor.pos(),
1959 false, cursor.boundary());
1960 selection.cursor = cursor;
1965 int const tmpheight = row->height();
1966 pos_type const tmplast = rowLast(row);
1970 breakAgain(bview, row);
1971 if (row->height() == tmpheight && rowLast(row) == tmplast)
1972 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1974 status(bview, LyXText::NEED_MORE_REFRESH);
1976 // check the special right address boxes
1977 if (textclasslist[bview->buffer()->params.textclass][
1978 par->layout()].margintype
1979 == MARGIN_RIGHT_ADDRESS_BOX)
1987 redoDrawingOfParagraph(bview, tmpcursor);
1990 // set the cursor again. Otherwise dangling pointers are possible
1991 // also set the selection
1993 if (selection.set()) {
1995 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1996 false, selection.cursor.boundary());
1997 selection.cursor = cursor;
1998 setCursorIntern(bview, selection.start.par(),
1999 selection.start.pos(),
2000 false, selection.start.boundary());
2001 selection.start = cursor;
2002 setCursorIntern(bview, selection.end.par(),
2003 selection.end.pos(),
2004 false, selection.end.boundary());
2005 selection.end = cursor;
2006 setCursorIntern(bview, last_sel_cursor.par(),
2007 last_sel_cursor.pos(),
2008 false, last_sel_cursor.boundary());
2009 last_sel_cursor = cursor;
2012 setCursorIntern(bview, cursor.par(), cursor.pos(),
2013 false, cursor.boundary());
2017 // returns false if inset wasn't found
2018 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2020 // first check the current paragraph
2021 int pos = cursor.par()->getPositionOfInset(inset);
2023 checkParagraph(bview, cursor.par(), pos);
2027 // check every paragraph
2029 Paragraph * par = ownerParagraph();
2031 pos = par->getPositionOfInset(inset);
2033 checkParagraph(bview, par, pos);
2043 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2045 bool setfont, bool boundary) const
2047 LyXCursor old_cursor = cursor;
2048 setCursorIntern(bview, par, pos, setfont, boundary);
2049 return deleteEmptyParagraphMechanism(bview, old_cursor);
2053 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2054 pos_type pos, bool boundary) const
2061 cur.boundary(boundary);
2063 // get the cursor y position in text
2065 Row * row = getRow(par, pos, y);
2066 Row * old_row = row;
2068 // if we are before the first char of this row and are still in the
2069 // same paragraph and there is a previous row then put the cursor on
2070 // the end of the previous row
2071 cur.iy(y + row->baseline());
2073 if (row->previous() && pos &&
2074 row->previous()->par() == row->par() &&
2075 par->getChar(pos) == Paragraph::META_INSET &&
2076 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2078 row = row->previous();
2083 // y is now the beginning of the cursor row
2084 y += row->baseline();
2085 // y is now the cursor baseline
2088 pos_type last = rowLastPrintable(old_row);
2090 if (pos > last + 1) {
2091 // This shouldn't happen.
2094 } else if (pos < row->pos()) {
2099 // now get the cursors x position
2100 float x = getCursorX(bview, row, pos, last, boundary);
2103 if (old_row != row) {
2104 x = getCursorX(bview, old_row, pos, last, boundary);
2111 float LyXText::getCursorX(BufferView * bview, Row * row,
2112 pos_type pos, pos_type last, bool boundary) const
2114 pos_type cursor_vpos = 0;
2116 float fill_separator;
2118 float fill_label_hfill;
2119 // This call HAS to be here because of the BidiTables!!!
2120 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2123 if (last < row->pos())
2124 cursor_vpos = row->pos();
2125 else if (pos > last && !boundary)
2126 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2127 ? row->pos() : last + 1;
2128 else if (pos > row->pos() &&
2129 (pos > last || boundary))
2130 /// Place cursor after char at (logical) position pos - 1
2131 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2132 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2134 /// Place cursor before char at (logical) position pos
2135 cursor_vpos = (bidi_level(pos) % 2 == 0)
2136 ? log2vis(pos) : log2vis(pos) + 1;
2138 pos_type main_body =
2139 beginningOfMainBody(bview->buffer(), row->par());
2140 if ((main_body > 0) &&
2141 ((main_body-1 > last) ||
2142 !row->par()->isLineSeparator(main_body-1)))
2145 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2146 pos_type pos = vis2log(vpos);
2147 if (main_body > 0 && pos == main_body - 1) {
2148 x += fill_label_hfill +
2149 lyxfont::width(textclasslist[
2150 bview->buffer()->params.textclass][
2151 row->par()->layout()]
2153 getLabelFont(bview->buffer(), row->par()));
2154 if (row->par()->isLineSeparator(main_body-1))
2155 x -= singleWidth(bview, row->par(),main_body-1);
2157 if (hfillExpansion(bview->buffer(), row, pos)) {
2158 x += singleWidth(bview, row->par(), pos);
2159 if (pos >= main_body)
2162 x += fill_label_hfill;
2163 } else if (row->par()->isSeparator(pos)) {
2164 x += singleWidth(bview, row->par(), pos);
2165 if (pos >= main_body)
2166 x += fill_separator;
2168 x += singleWidth(bview, row->par(), pos);
2174 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2175 pos_type pos, bool setfont, bool boundary) const
2177 InsetText * it = static_cast<InsetText *>(par->inInset());
2179 if (it != inset_owner) {
2180 lyxerr[Debug::INSETS] << "InsetText is " << it
2182 << "inset_owner is "
2183 << inset_owner << endl;
2184 #ifdef WITH_WARNINGS
2185 #warning I believe this code is wrong. (Lgb)
2186 #warning Jürgen, have a look at this. (Lgb)
2187 #warning Hmmm, I guess you are right but we
2188 #warning should verify when this is needed
2190 // Jürgen, would you like to have a look?
2191 // I guess we need to move the outer cursor
2192 // and open and lock the inset (bla bla bla)
2193 // stuff I don't know... so can you have a look?
2195 // I moved the lyxerr stuff in here so we can see if
2196 // this is actually really needed and where!
2198 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2203 setCursor(bview, cursor, par, pos, boundary);
2205 setCurrentFont(bview);
2209 void LyXText::setCurrentFont(BufferView * bview) const
2211 pos_type pos = cursor.pos();
2212 if (cursor.boundary() && pos > 0)
2216 if (pos == cursor.par()->size())
2218 else // potentional bug... BUG (Lgb)
2219 if (cursor.par()->isSeparator(pos)) {
2220 if (pos > cursor.row()->pos() &&
2221 bidi_level(pos) % 2 ==
2222 bidi_level(pos - 1) % 2)
2224 else if (pos + 1 < cursor.par()->size())
2230 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2231 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2233 if (cursor.pos() == cursor.par()->size() &&
2234 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2235 !cursor.boundary()) {
2236 Language const * lang =
2237 cursor.par()->getParLanguage(bview->buffer()->params);
2238 current_font.setLanguage(lang);
2239 current_font.setNumber(LyXFont::OFF);
2240 real_current_font.setLanguage(lang);
2241 real_current_font.setNumber(LyXFont::OFF);
2246 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2248 LyXCursor old_cursor = cursor;
2250 setCursorFromCoordinates(bview, cursor, x, y);
2251 setCurrentFont(bview);
2252 deleteEmptyParagraphMechanism(bview, old_cursor);
2256 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2259 // Get the row first.
2261 Row * row = getRowNearY(y);
2263 pos_type const column = getColumnNearX(bview, row, x, bound);
2264 cur.par(row->par());
2265 cur.pos(row->pos() + column);
2267 cur.y(y + row->baseline());
2270 if (row->next() && row->next()->pos() == cur.pos() &&
2271 cur.par() == row->next()->par() &&
2272 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2273 (ins=cur.par()->getInset(cur.pos())) &&
2274 (ins->needFullRow() || ins->display()))
2276 // we enter here if we put the cursor on the end of the row before
2277 // a inset which uses a full row and in that case we HAVE to calculate
2278 // the right (i) values.
2279 pos_type last = rowLastPrintable(row);
2280 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2282 cur.iy(y + row->height() + row->next()->baseline());
2283 cur.irow(row->next());
2289 cur.boundary(bound);
2293 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2295 if (cursor.pos() > 0) {
2296 bool boundary = cursor.boundary();
2297 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2298 if (!internal && !boundary &&
2299 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2300 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2301 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2302 Paragraph * par = cursor.par()->previous();
2303 setCursor(bview, par, par->size());
2308 void LyXText::cursorRight(BufferView * bview, bool internal) const
2310 if (!internal && cursor.boundary() &&
2311 !cursor.par()->isNewline(cursor.pos()))
2312 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2313 else if (cursor.pos() < cursor.par()->size()) {
2314 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2316 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2317 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2318 } else if (cursor.par()->next())
2319 setCursor(bview, cursor.par()->next(), 0);
2323 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2326 int x = cursor.x_fix();
2327 int y = cursor.y() - cursor.row()->baseline() - 1;
2328 setCursorFromCoordinates(bview, x, y);
2330 int y1 = cursor.iy() - first_y;
2334 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2335 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2336 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2340 setCursorFromCoordinates(bview, cursor.x_fix(),
2341 cursor.y() - cursor.row()->baseline() - 1);
2346 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2349 int x = cursor.x_fix();
2350 int y = cursor.y() - cursor.row()->baseline() +
2351 cursor.row()->height() + 1;
2352 setCursorFromCoordinates(bview, x, y);
2353 if (!selecting && cursor.row() == cursor.irow()) {
2354 int y1 = cursor.iy() - first_y;
2358 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2359 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2360 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2364 setCursorFromCoordinates(bview, cursor.x_fix(),
2365 cursor.y() - cursor.row()->baseline()
2366 + cursor.row()->height() + 1);
2371 void LyXText::cursorUpParagraph(BufferView * bview) const
2373 if (cursor.pos() > 0) {
2374 setCursor(bview, cursor.par(), 0);
2376 else if (cursor.par()->previous()) {
2377 setCursor(bview, cursor.par()->previous(), 0);
2382 void LyXText::cursorDownParagraph(BufferView * bview) const
2384 if (cursor.par()->next()) {
2385 setCursor(bview, cursor.par()->next(), 0);
2387 setCursor(bview, cursor.par(), cursor.par()->size());
2391 // fix the cursor `cur' after a characters has been deleted at `where'
2392 // position. Called by deleteEmptyParagraphMechanism
2393 void LyXText::fixCursorAfterDelete(BufferView * bview,
2395 LyXCursor const & where) const
2397 // if cursor is not in the paragraph where the delete occured,
2399 if (cur.par() != where.par())
2402 // if cursor position is after the place where the delete occured,
2404 if (cur.pos() > where.pos())
2405 cur.pos(cur.pos()-1);
2407 // check also if we don't want to set the cursor on a spot behind the
2408 // pagragraph because we erased the last character.
2409 if (cur.pos() > cur.par()->size())
2410 cur.pos(cur.par()->size());
2412 // recompute row et al. for this cursor
2413 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2417 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2418 LyXCursor const & old_cursor) const
2420 // Would be wrong to delete anything if we have a selection.
2421 if (selection.set())
2424 // We allow all kinds of "mumbo-jumbo" when freespacing.
2425 if (textclasslist[bview->buffer()->params.textclass][
2426 old_cursor.par()->layout()].free_spacing
2427 || old_cursor.par()->isFreeSpacing())
2432 /* Ok I'll put some comments here about what is missing.
2433 I have fixed BackSpace (and thus Delete) to not delete
2434 double-spaces automagically. I have also changed Cut,
2435 Copy and Paste to hopefully do some sensible things.
2436 There are still some small problems that can lead to
2437 double spaces stored in the document file or space at
2438 the beginning of paragraphs. This happens if you have
2439 the cursor betwenn to spaces and then save. Or if you
2440 cut and paste and the selection have a space at the
2441 beginning and then save right after the paste. I am
2442 sure none of these are very hard to fix, but I will
2443 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2444 that I can get some feedback. (Lgb)
2447 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2448 // delete the LineSeparator.
2451 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2452 // delete the LineSeparator.
2455 // If the pos around the old_cursor were spaces, delete one of them.
2456 if (old_cursor.par() != cursor.par()
2457 || old_cursor.pos() != cursor.pos()) {
2458 // Only if the cursor has really moved
2460 if (old_cursor.pos() > 0
2461 && old_cursor.pos() < old_cursor.par()->size()
2462 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2463 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2464 old_cursor.par()->erase(old_cursor.pos() - 1);
2465 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2467 #ifdef WITH_WARNINGS
2468 #warning This will not work anymore when we have multiple views of the same buffer
2469 // In this case, we will have to correct also the cursors held by
2470 // other bufferviews. It will probably be easier to do that in a more
2471 // automated way in LyXCursor code. (JMarc 26/09/2001)
2473 // correct all cursors held by the LyXText
2474 fixCursorAfterDelete(bview, cursor, old_cursor);
2475 fixCursorAfterDelete(bview, selection.cursor,
2477 fixCursorAfterDelete(bview, selection.start,
2479 fixCursorAfterDelete(bview, selection.end, old_cursor);
2480 fixCursorAfterDelete(bview, last_sel_cursor,
2482 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2483 fixCursorAfterDelete(bview, toggle_end_cursor,
2489 // don't delete anything if this is the ONLY paragraph!
2490 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2493 // Do not delete empty paragraphs with keepempty set.
2495 [bview->buffer()->params.textclass]
2496 [old_cursor.par()->layout()].keepempty)
2499 // only do our magic if we changed paragraph
2500 if (old_cursor.par() == cursor.par())
2503 // record if we have deleted a paragraph
2504 // we can't possibly have deleted a paragraph before this point
2505 bool deleted = false;
2507 if ((old_cursor.par()->size() == 0
2508 || (old_cursor.par()->size() == 1
2509 && old_cursor.par()->isLineSeparator(0)))) {
2510 // ok, we will delete anything
2511 LyXCursor tmpcursor;
2513 // make sure that you do not delete any environments
2514 status(bview, LyXText::NEED_MORE_REFRESH);
2517 if (old_cursor.row()->previous()) {
2518 refresh_row = old_cursor.row()->previous();
2519 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2521 cursor = old_cursor; // that undo can restore the right cursor position
2522 Paragraph * endpar = old_cursor.par()->next();
2523 if (endpar && endpar->getDepth()) {
2524 while (endpar && endpar->getDepth()) {
2525 endpar = endpar->next();
2528 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2532 removeRow(old_cursor.row());
2533 if (ownerParagraph() == old_cursor.par()) {
2534 ownerParagraph(ownerParagraph()->next());
2537 delete old_cursor.par();
2539 /* Breakagain the next par. Needed because of
2540 * the parindent that can occur or dissappear.
2541 * The next row can change its height, if
2542 * there is another layout before */
2543 if (refresh_row->next()) {
2544 breakAgain(bview, refresh_row->next());
2545 updateCounters(bview, refresh_row);
2547 setHeightOfRow(bview, refresh_row);
2549 refresh_row = old_cursor.row()->next();
2550 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2553 cursor = old_cursor; // that undo can restore the right cursor position
2554 Paragraph * endpar = old_cursor.par()->next();
2555 if (endpar && endpar->getDepth()) {
2556 while (endpar && endpar->getDepth()) {
2557 endpar = endpar->next();
2560 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2564 removeRow(old_cursor.row());
2566 if (ownerParagraph() == old_cursor.par()) {
2567 ownerParagraph(ownerParagraph()->next());
2570 delete old_cursor.par();
2572 /* Breakagain the next par. Needed because of
2573 the parindent that can occur or dissappear.
2574 The next row can change its height, if
2575 there is another layout before */
2577 breakAgain(bview, refresh_row);
2578 updateCounters(bview, refresh_row->previous());
2583 setCursorIntern(bview, cursor.par(), cursor.pos());
2585 if (selection.cursor.par() == old_cursor.par()
2586 && selection.cursor.pos() == old_cursor.pos()) {
2587 // correct selection
2588 selection.cursor = cursor;
2592 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2593 redoParagraphs(bview, old_cursor,
2594 old_cursor.par()->next());
2596 setCursorIntern(bview, cursor.par(), cursor.pos());
2597 selection.cursor = cursor;
2604 void LyXText::toggleAppendix(BufferView * bview)
2606 Paragraph * par = cursor.par();
2607 bool start = !par->params().startOfAppendix();
2609 // ensure that we have only one start_of_appendix in this document
2610 Paragraph * tmp = ownerParagraph();
2611 for (; tmp; tmp = tmp->next()) {
2612 tmp->params().startOfAppendix(false);
2615 par->params().startOfAppendix(start);
2617 // we can set the refreshing parameters now
2618 status(bview, LyXText::NEED_MORE_REFRESH);
2620 refresh_row = 0; // not needed for full update
2621 updateCounters(bview, 0);
2622 setCursor(bview, cursor.par(), cursor.pos());
2626 Paragraph * LyXText::ownerParagraph() const
2629 return inset_owner->paragraph();
2631 return bv_owner->buffer()->paragraph;
2635 void LyXText::ownerParagraph(Paragraph * p) const
2638 inset_owner->paragraph(p);
2640 bv_owner->buffer()->paragraph = p;
2645 void LyXText::ownerParagraph(int id, Paragraph * p) const
2647 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2648 if (op && op->inInset()) {
2649 static_cast<InsetText *>(op->inInset())->paragraph(p);
2656 LyXText::text_status LyXText::status() const
2662 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2664 // We should only go up with refreshing code so this means that if
2665 // we have a MORE refresh we should never set it to LITTLE if we still
2666 // didn't handle it (and then it will be UNCHANGED. Now as long as
2667 // we stay inside one LyXText this may work but we need to tell the
2668 // outermost LyXText that it should REALLY draw us if there is some
2669 // change in a Inset::LyXText. So you see that when we are inside a
2670 // inset's LyXText we give the LITTLE to the outermost LyXText to
2671 // tell'em that it should redraw the actual row (where the inset
2672 // resides! Capito?!
2674 if ((status_ != NEED_MORE_REFRESH)
2675 || (status_ == NEED_MORE_REFRESH
2676 && st != NEED_VERY_LITTLE_REFRESH))
2679 if (inset_owner && st != UNCHANGED) {
2680 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2681 if (!bview->text->refresh_row) {
2682 bview->text->refresh_row = bview->text->cursor.row();
2683 bview->text->refresh_y = bview->text->cursor.y() -
2684 bview->text->cursor.row()->baseline();