1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
27 #include "support/textutils.h"
28 #include "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
41 #include "FloatList.h"
43 #include "ParagraphParameters.h"
52 LyXText::LyXText(BufferView * bv)
53 : number_of_rows(0), height(0), width(0), first(0),
54 bv_owner(bv), inset_owner(0), the_locking_inset(0),
55 need_break_row(0), refresh_y(0), refresh_row(0),
56 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
61 LyXText::LyXText(InsetText * inset)
62 : number_of_rows(0), height(0), width(0), first(0),
63 bv_owner(0), inset_owner(inset), the_locking_inset(0),
64 need_break_row(0), refresh_y(0), refresh_row(0),
65 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
69 void LyXText::init(BufferView * bview, bool reinit)
72 // Delete all rows, this does not touch the paragraphs!
73 Row * tmprow = firstrow;
75 tmprow = firstrow->next();
79 lastrow = refresh_row = need_break_row = 0;
80 width = height = copylayouttype = 0;
81 number_of_rows = first = refresh_y = 0;
82 status_ = LyXText::UNCHANGED;
86 Paragraph * par = ownerParagraph();
87 current_font = getFont(bview->buffer(), par, 0);
89 insertParagraph(bview, par, lastrow);
92 setCursorIntern(bview, firstrow->par(), 0);
93 selection.cursor = cursor;
99 // Delete all rows, this does not touch the paragraphs!
100 Row * tmprow = firstrow;
102 tmprow = firstrow->next();
109 // Gets the fully instantiated font at a given position in a paragraph
110 // Basically the same routine as Paragraph::getFont() in paragraph.C.
111 // The difference is that this one is used for displaying, and thus we
112 // are allowed to make cosmetic improvements. For instance make footnotes
114 // If position is -1, we get the layout font of the paragraph.
115 // If position is -2, we get the font of the manual label of the paragraph.
116 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
117 Paragraph::size_type pos) const
119 LyXLayout const & layout =
120 textclasslist.Style(buf->params.textclass, par->getLayout());
122 Paragraph::depth_type par_depth = par->getDepth();
123 // We specialize the 95% common case:
127 if (layout.labeltype == LABEL_MANUAL
128 && pos < beginningOfMainBody(buf, par)) {
130 LyXFont f = par->getFontSettings(buf->params,
132 return f.realize(layout.reslabelfont, buf->params.language);
134 LyXFont f = par->getFontSettings(buf->params, pos);
135 return f.realize(layout.resfont, buf->params.language);
140 // process layoutfont for pos == -1 and labelfont for pos < -1
142 return layout.resfont;
144 return layout.reslabelfont;
148 // The uncommon case need not be optimized as much
150 LyXFont layoutfont, tmpfont;
154 if (pos < beginningOfMainBody(buf, par)) {
156 layoutfont = layout.labelfont;
159 layoutfont = layout.font;
161 tmpfont = par->getFontSettings(buf->params, pos);
162 tmpfont.realize(layoutfont, buf->params.language);
165 // process layoutfont for pos == -1 and labelfont for pos < -1
167 tmpfont = layout.font;
169 tmpfont = layout.labelfont;
172 // Resolve against environment font information
173 while (par && par_depth && !tmpfont.resolved()) {
174 par = par->outerHook();
176 tmpfont.realize(textclasslist.
177 Style(buf->params.textclass,
178 par->getLayout()).font,
179 buf->params.language);
180 par_depth = par->getDepth();
184 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
185 buf->params.language);
191 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
192 Paragraph::size_type pos, LyXFont const & fnt,
195 Buffer const * buf = bv->buffer();
196 LyXFont font = getFont(buf, par, pos);
197 font.update(fnt, toggleall);
198 // Let the insets convert their font
199 if (par->getChar(pos) == Paragraph::META_INSET) {
200 Inset * inset = par->getInset(pos);
202 if (inset->editable()==Inset::IS_EDITABLE) {
203 UpdatableInset * uinset =
204 static_cast<UpdatableInset *>(inset);
205 uinset->setFont(bv, fnt, toggleall, true);
207 font = inset->convertFont(font);
211 LyXLayout const & layout =
212 textclasslist.Style(buf->params.textclass,
215 // Get concrete layout font to reduce against
218 if (pos < beginningOfMainBody(buf, par))
219 layoutfont = layout.labelfont;
221 layoutfont = layout.font;
223 // Realize against environment font information
224 if (par->getDepth()){
225 Paragraph * tp = par;
226 while (!layoutfont.resolved() && tp && tp->getDepth()) {
227 tp = tp->outerHook();
229 layoutfont.realize(textclasslist.
230 Style(buf->params.textclass,
231 tp->getLayout()).font,
232 buf->params.language);
236 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
237 buf->params.language);
239 // Now, reduce font against full layout font
240 font.reduce(layoutfont);
242 par->setFont(pos, font);
246 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
247 Paragraph::size_type pos, LyXFont const & fnt)
250 // Let the insets convert their font
251 if (par->getChar(pos) == Paragraph::META_INSET) {
252 font = par->getInset(pos)->convertFont(font);
255 LyXLayout const & layout =
256 textclasslist.Style(buf->params.textclass,
259 // Get concrete layout font to reduce against
262 if (pos < beginningOfMainBody(buf, par))
263 layoutfont = layout.labelfont;
265 layoutfont = layout.font;
267 // Realize against environment font information
268 if (par->getDepth()){
269 Paragraph * tp = par;
270 while (!layoutfont.resolved() && tp && tp->getDepth()) {
271 tp = tp->outerHook();
273 layoutfont.realize(textclasslist.
274 Style(buf->params.textclass,
275 tp->getLayout()).font,
276 buf->params.language);
280 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
281 buf->params.language);
283 // Now, reduce font against full layout font
284 font.reduce(layoutfont);
286 par->setFont(pos, font);
290 // inserts a new row behind the specified row, increments
291 // the touched counters
292 void LyXText::insertRow(Row * row, Paragraph * par,
293 Paragraph::size_type pos) const
295 Row * tmprow = new Row;
298 tmprow->next(firstrow);
301 tmprow->previous(row);
302 tmprow->next(row->next());
307 tmprow->next()->previous(tmprow);
309 if (tmprow->previous())
310 tmprow->previous()->next(tmprow);
322 // removes the row and reset the touched counters
323 void LyXText::removeRow(Row * row) const
325 /* this must not happen before the currentrow for clear reasons.
326 so the trick is just to set the current row onto the previous
329 getRow(row->par(), row->pos(), unused_y);
332 row->next()->previous(row->previous());
333 if (!row->previous()) {
334 firstrow = row->next();
336 row->previous()->next(row->next());
339 lastrow = row->previous();
341 height -= row->height(); // the text becomes smaller
344 --number_of_rows; // one row less
348 // remove all following rows of the paragraph of the specified row.
349 void LyXText::removeParagraph(Row * row) const
351 Paragraph * tmppar = row->par();
355 while (row && row->par() == tmppar) {
356 tmprow = row->next();
363 // insert the specified paragraph behind the specified row
364 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
367 insertRow(row, par, 0); /* insert a new row, starting
370 setCounter(bview->buffer(), par); // set the counters
372 // and now append the whole paragraph behind the new row
375 appendParagraph(bview, firstrow);
377 row->next()->height(0);
378 appendParagraph(bview, row->next());
383 Inset * LyXText::getInset() const
386 if (cursor.pos() == 0 && cursor.par()->bibkey) {
387 inset = cursor.par()->bibkey;
388 } else if (cursor.pos() < cursor.par()->size()
389 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
390 inset = cursor.par()->getInset(cursor.pos());
396 void LyXText::toggleInset(BufferView * bview)
398 Inset * inset = getInset();
399 if (!inset->editable())
401 //bview->owner()->message(inset->editMessage());
403 // do we want to keep this?? (JMarc)
404 if (inset->editable() != Inset::HIGHLY_EDITABLE)
405 setCursorParUndo(bview);
407 if (inset->isOpen()) {
413 inset->open(bview, !inset->isOpen());
418 /* used in setlayout */
419 // Asger is not sure we want to do this...
420 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
423 LyXLayout const & layout =
424 textclasslist.Style(buf->params.textclass, par->getLayout());
427 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
428 if (pos < beginningOfMainBody(buf, par))
429 layoutfont = layout.labelfont;
431 layoutfont = layout.font;
433 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
434 tmpfont.reduce(layoutfont);
435 par->setFont(pos, tmpfont);
440 Paragraph * LyXText::setLayout(BufferView * bview,
441 LyXCursor & cur, LyXCursor & sstart_cur,
442 LyXCursor & send_cur,
443 LyXTextClass::size_type layout)
445 Paragraph * endpar = send_cur.par()->next();
446 Paragraph * undoendpar = endpar;
448 if (endpar && endpar->getDepth()) {
449 while (endpar && endpar->getDepth()) {
450 endpar = endpar->next();
454 endpar = endpar->next(); // because of parindents etc.
457 setUndo(bview, Undo::EDIT,
458 sstart_cur.par(), undoendpar);
460 // ok we have a selection. This is always between sstart_cur
461 // and sel_end cursor
464 LyXLayout const & lyxlayout =
465 textclasslist.Style(bview->buffer()->params.textclass, layout);
467 while (cur.par() != send_cur.par()) {
468 cur.par()->setLayout(layout);
469 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
470 Paragraph * fppar = cur.par();
471 fppar->params().spaceTop(lyxlayout.fill_top ?
472 VSpace(VSpace::VFILL)
473 : VSpace(VSpace::NONE));
474 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
475 VSpace(VSpace::VFILL)
476 : VSpace(VSpace::NONE));
477 if (lyxlayout.margintype == MARGIN_MANUAL)
478 cur.par()->setLabelWidthString(lyxlayout.labelstring());
479 if (lyxlayout.labeltype != LABEL_BIBLIO
481 delete fppar->bibkey;
484 cur.par(cur.par()->next());
486 cur.par()->setLayout(layout);
487 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
488 Paragraph * fppar = cur.par();
489 fppar->params().spaceTop(lyxlayout.fill_top ?
490 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
491 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
492 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
493 if (lyxlayout.margintype == MARGIN_MANUAL)
494 cur.par()->setLabelWidthString(lyxlayout.labelstring());
495 if (lyxlayout.labeltype != LABEL_BIBLIO
497 delete fppar->bibkey;
504 // set layout over selection and make a total rebreak of those paragraphs
505 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
507 LyXCursor tmpcursor = cursor; /* store the current cursor */
509 // if there is no selection just set the layout
510 // of the current paragraph */
511 if (!selection.set()) {
512 selection.start = cursor; // dummy selection
513 selection.end = cursor;
515 Paragraph * endpar = setLayout(bview, cursor, selection.start,
516 selection.end, layout);
517 redoParagraphs(bview, selection.start, endpar);
519 // we have to reset the selection, because the
520 // geometry could have changed
521 setCursor(bview, selection.start.par(),
522 selection.start.pos(), false);
523 selection.cursor = cursor;
524 setCursor(bview, selection.end.par(), selection.end.pos(), false);
525 updateCounters(bview, cursor.row());
528 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
532 // increment depth over selection and
533 // make a total rebreak of those paragraphs
534 void LyXText::incDepth(BufferView * bview)
536 // If there is no selection, just use the current paragraph
537 if (!selection.set()) {
538 selection.start = cursor; // dummy selection
539 selection.end = cursor;
542 // We end at the next paragraph with depth 0
543 Paragraph * endpar = selection.end.par()->next();
545 Paragraph * undoendpar = endpar;
547 if (endpar && endpar->getDepth()) {
548 while (endpar && endpar->getDepth()) {
549 endpar = endpar->next();
553 endpar = endpar->next(); // because of parindents etc.
556 setUndo(bview, Undo::EDIT,
557 selection.start.par(), undoendpar);
559 LyXCursor tmpcursor = cursor; // store the current cursor
561 // ok we have a selection. This is always between sel_start_cursor
562 // and sel_end cursor
563 cursor = selection.start;
565 bool anything_changed = false;
568 // NOTE: you can't change the depth of a bibliography entry
570 textclasslist.Style(bview->buffer()->params.textclass,
571 cursor.par()->getLayout()
572 ).labeltype != LABEL_BIBLIO) {
573 Paragraph * prev = cursor.par()->previous();
576 && (prev->getDepth() - cursor.par()->getDepth() > 0
577 || (prev->getDepth() == cursor.par()->getDepth()
578 && textclasslist.Style(bview->buffer()->params.textclass,
579 prev->getLayout()).isEnvironment()))) {
580 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
581 anything_changed = true;
584 if (cursor.par() == selection.end.par())
586 cursor.par(cursor.par()->next());
589 // if nothing changed set all depth to 0
590 if (!anything_changed) {
591 cursor = selection.start;
592 while (cursor.par() != selection.end.par()) {
593 cursor.par()->params().depth(0);
594 cursor.par(cursor.par()->next());
596 cursor.par()->params().depth(0);
599 redoParagraphs(bview, selection.start, endpar);
601 // we have to reset the selection, because the
602 // geometry could have changed
603 setCursor(bview, selection.start.par(), selection.start.pos());
604 selection.cursor = cursor;
605 setCursor(bview, selection.end.par(), selection.end.pos());
606 updateCounters(bview, cursor.row());
609 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
613 // decrement depth over selection and
614 // make a total rebreak of those paragraphs
615 void LyXText::decDepth(BufferView * bview)
617 // if there is no selection just set the layout
618 // of the current paragraph
619 if (!selection.set()) {
620 selection.start = cursor; // dummy selection
621 selection.end = cursor;
623 Paragraph * endpar = selection.end.par()->next();
624 Paragraph * undoendpar = endpar;
626 if (endpar && endpar->getDepth()) {
627 while (endpar && endpar->getDepth()) {
628 endpar = endpar->next();
632 endpar = endpar->next(); // because of parindents etc.
635 setUndo(bview, Undo::EDIT,
636 selection.start.par(), undoendpar);
638 LyXCursor tmpcursor = cursor; // store the current cursor
640 // ok we have a selection. This is always between sel_start_cursor
641 // and sel_end cursor
642 cursor = selection.start;
645 if (cursor.par()->params().depth()) {
646 cursor.par()->params()
647 .depth(cursor.par()->params().depth() - 1);
649 if (cursor.par() == selection.end.par()) {
652 cursor.par(cursor.par()->next());
655 redoParagraphs(bview, selection.start, endpar);
657 // we have to reset the selection, because the
658 // geometry could have changed
659 setCursor(bview, selection.start.par(),
660 selection.start.pos());
661 selection.cursor = cursor;
662 setCursor(bview, selection.end.par(), selection.end.pos());
663 updateCounters(bview, cursor.row());
666 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
670 // set font over selection and make a total rebreak of those paragraphs
671 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
673 // if there is no selection just set the current_font
674 if (!selection.set()) {
675 // Determine basis font
677 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
680 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
682 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
684 // Update current font
685 real_current_font.update(font, toggleall);
687 // Reduce to implicit settings
688 current_font = real_current_font;
689 current_font.reduce(layoutfont);
690 // And resolve it completely
691 real_current_font.realize(layoutfont,
692 bview->buffer()->params.language);
696 LyXCursor tmpcursor = cursor; // store the current cursor
698 // ok we have a selection. This is always between sel_start_cursor
699 // and sel_end cursor
701 setUndo(bview, Undo::EDIT,
702 selection.start.par(), selection.end.par()->next());
704 cursor = selection.start;
705 while (cursor.par() != selection.end.par() ||
706 (cursor.pos() < selection.end.pos()))
708 if (cursor.pos() < cursor.par()->size()) {
709 // an open footnote should behave
711 setCharFont(bview, cursor.par(), cursor.pos(),
713 cursor.pos(cursor.pos() + 1);
716 cursor.par(cursor.par()->next());
721 redoParagraphs(bview, selection.start, selection.end.par()->next());
723 // we have to reset the selection, because the
724 // geometry could have changed
725 setCursor(bview, selection.start.par(), selection.start.pos());
726 selection.cursor = cursor;
727 setCursor(bview, selection.end.par(), selection.end.pos());
730 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
731 tmpcursor.boundary());
735 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
737 Row * tmprow = cur.row();
738 int y = cur.y() - tmprow->baseline();
740 setHeightOfRow(bview, tmprow);
742 while (tmprow->previous()
743 && tmprow->previous()->par() == tmprow->par()) {
744 tmprow = tmprow->previous();
745 y -= tmprow->height();
746 setHeightOfRow(bview, tmprow);
749 // we can set the refreshing parameters now
750 status(bview, LyXText::NEED_MORE_REFRESH);
752 refresh_row = tmprow;
753 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
757 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
759 Row * tmprow = cur.row();
761 int y = cur.y() - tmprow->baseline();
762 setHeightOfRow(bview, tmprow);
764 while (tmprow->previous()
765 && tmprow->previous()->par() == tmprow->par()) {
766 tmprow = tmprow->previous();
767 y -= tmprow->height();
770 // we can set the refreshing parameters now
771 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
773 refresh_row = tmprow;
775 status(bview, LyXText::NEED_MORE_REFRESH);
776 setCursor(bview, cur.par(), cur.pos());
780 // deletes and inserts again all paragaphs between the cursor
781 // and the specified par
782 // This function is needed after SetLayout and SetFont etc.
783 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
784 Paragraph const * endpar) const
787 Paragraph * tmppar = 0;
788 Paragraph * first_phys_par = 0;
790 Row * tmprow = cur.row();
792 int y = cur.y() - tmprow->baseline();
794 if (!tmprow->previous()) {
795 // a trick/hack for UNDO
796 // Can somebody please tell me _why_ this solves
798 first_phys_par = firstParagraph();
800 first_phys_par = tmprow->par();
801 while (tmprow->previous()
802 && tmprow->previous()->par() == first_phys_par)
804 tmprow = tmprow->previous();
805 y -= tmprow->height();
809 // we can set the refreshing parameters now
810 status(bview, LyXText::NEED_MORE_REFRESH);
812 refresh_row = tmprow->previous(); /* the real refresh row will
813 be deleted, so I store
817 tmppar = tmprow->next()->par();
820 while (tmppar != endpar) {
821 removeRow(tmprow->next());
823 tmppar = tmprow->next()->par();
828 // remove the first one
829 tmprow2 = tmprow; /* this is because tmprow->previous()
831 tmprow = tmprow->previous();
834 tmppar = first_phys_par;
838 insertParagraph(bview, tmppar, tmprow);
842 while (tmprow->next()
843 && tmprow->next()->par() == tmppar) {
844 tmprow = tmprow->next();
846 tmppar = tmppar->next();
848 } while (tmppar && tmppar != endpar);
850 // this is because of layout changes
852 refresh_y -= refresh_row->height();
853 setHeightOfRow(bview, refresh_row);
855 refresh_row = firstrow;
857 setHeightOfRow(bview, refresh_row);
860 if (tmprow && tmprow->next())
861 setHeightOfRow(bview, tmprow->next());
865 bool LyXText::fullRebreak(BufferView * bview)
871 if (need_break_row) {
872 breakAgain(bview, need_break_row);
880 // important for the screen
883 /* the cursor set functions have a special mechanism. When they
884 * realize, that you left an empty paragraph, they will delete it.
885 * They also delete the corresponding row */
887 // need the selection cursor:
888 void LyXText::setSelection(BufferView * bview)
890 bool const lsel = selection.set();
892 if (!selection.set()) {
893 last_sel_cursor = selection.cursor;
894 selection.start = selection.cursor;
895 selection.end = selection.cursor;
900 // first the toggling area
901 if (cursor.y() < last_sel_cursor.y()
902 || (cursor.y() == last_sel_cursor.y()
903 && cursor.x() < last_sel_cursor.x())) {
904 toggle_end_cursor = last_sel_cursor;
905 toggle_cursor = cursor;
907 toggle_end_cursor = cursor;
908 toggle_cursor = last_sel_cursor;
911 last_sel_cursor = cursor;
913 // and now the whole selection
915 if (selection.cursor.par() == cursor.par())
916 if (selection.cursor.pos() < cursor.pos()) {
917 selection.end = cursor;
918 selection.start = selection.cursor;
920 selection.end = selection.cursor;
921 selection.start = cursor;
923 else if (selection.cursor.y() < cursor.y() ||
924 (selection.cursor.y() == cursor.y()
925 && selection.cursor.x() < cursor.x())) {
926 selection.end = cursor;
927 selection.start = selection.cursor;
930 selection.end = selection.cursor;
931 selection.start = cursor;
934 // a selection with no contents is not a selection
935 if (selection.start.par() == selection.end.par() &&
936 selection.start.pos() == selection.end.pos())
937 selection.set(false);
939 if (inset_owner && (selection.set() || lsel))
940 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
944 string const LyXText::selectionAsString(Buffer const * buffer) const
946 if (!selection.set()) return string();
949 // Special handling if the whole selection is within one paragraph
950 if (selection.start.par() == selection.end.par()) {
951 result += selection.start.par()->asString(buffer,
952 selection.start.pos(),
953 selection.end.pos());
957 // The selection spans more than one paragraph
959 // First paragraph in selection
960 result += selection.start.par()->asString(buffer,
961 selection.start.pos(),
962 selection.start.par()->size())
965 // The paragraphs in between (if any)
966 LyXCursor tmpcur(selection.start);
967 tmpcur.par(tmpcur.par()->next());
968 while (tmpcur.par() != selection.end.par()) {
969 result += tmpcur.par()->asString(buffer, 0,
970 tmpcur.par()->size()) +"\n\n";
971 tmpcur.par(tmpcur.par()->next());
974 // Last paragraph in selection
975 result += selection.end.par()->asString(buffer, 0,
976 selection.end.pos());
982 void LyXText::clearSelection() const
984 selection.set(false);
985 selection.mark(false);
986 selection.end = selection.start = selection.cursor = cursor;
990 void LyXText::cursorHome(BufferView * bview) const
992 setCursor(bview, cursor.par(), cursor.row()->pos());
996 void LyXText::cursorEnd(BufferView * bview) const
998 if (!cursor.row()->next()
999 || cursor.row()->next()->par() != cursor.row()->par()) {
1000 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1002 if (cursor.par()->size() &&
1003 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1004 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1005 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1007 setCursor(bview,cursor.par(),
1008 rowLast(cursor.row()) + 1);
1014 void LyXText::cursorTop(BufferView * bview) const
1016 while (cursor.par()->previous())
1017 cursor.par(cursor.par()->previous());
1018 setCursor(bview, cursor.par(), 0);
1022 void LyXText::cursorBottom(BufferView * bview) const
1024 while (cursor.par()->next())
1025 cursor.par(cursor.par()->next());
1026 setCursor(bview, cursor.par(), cursor.par()->size());
1030 void LyXText::toggleFree(BufferView * bview,
1031 LyXFont const & font, bool toggleall)
1033 // If the mask is completely neutral, tell user
1034 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1035 // Could only happen with user style
1036 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1040 // Try implicit word selection
1041 // If there is a change in the language the implicit word selection
1043 LyXCursor resetCursor = cursor;
1044 bool implicitSelection = (font.language() == ignore_language
1045 && font.number() == LyXFont::IGNORE)
1046 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1049 setFont(bview, font, toggleall);
1051 // Implicit selections are cleared afterwards
1052 //and cursor is set to the original position.
1053 if (implicitSelection) {
1055 cursor = resetCursor;
1056 setCursor(bview, cursor.par(), cursor.pos());
1057 selection.cursor = cursor;
1060 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1065 LyXText::getStringToIndex(BufferView * bview)
1069 // Try implicit word selection
1070 // If there is a change in the language the implicit word selection
1072 LyXCursor resetCursor = cursor;
1073 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1075 if (!selection.set()) {
1076 bview->owner()->message(_("Nothing to index!"));
1079 if (selection.start.par() != selection.end.par()) {
1080 bview->owner()->message(_("Cannot index more than one paragraph!"));
1084 idxstring = selectionAsString(bview->buffer());
1086 // Implicit selections are cleared afterwards
1087 //and cursor is set to the original position.
1088 if (implicitSelection) {
1090 cursor = resetCursor;
1091 setCursor(bview, cursor.par(), cursor.pos());
1092 selection.cursor = cursor;
1097 Paragraph::size_type
1098 LyXText::beginningOfMainBody(Buffer const * buf,
1099 Paragraph const * par) const
1101 if (textclasslist.Style(buf->params.textclass,
1102 par->getLayout()).labeltype != LABEL_MANUAL)
1105 return par->beginningOfMainBody();
1109 /* the DTP switches for paragraphs. LyX will store them in the
1110 * first physicla paragraph. When a paragraph is broken, the top settings
1111 * rest, the bottom settings are given to the new one. So I can make shure,
1112 * they do not duplicate themself and you cannnot make dirty things with
1115 void LyXText::setParagraph(BufferView * bview,
1116 bool line_top, bool line_bottom,
1117 bool pagebreak_top, bool pagebreak_bottom,
1118 VSpace const & space_top,
1119 VSpace const & space_bottom,
1121 string labelwidthstring,
1124 LyXCursor tmpcursor = cursor;
1125 if (!selection.set()) {
1126 selection.start = cursor;
1127 selection.end = cursor;
1130 // make sure that the depth behind the selection are restored, too
1131 Paragraph * endpar = selection.end.par()->next();
1132 Paragraph * undoendpar = endpar;
1134 if (endpar && endpar->getDepth()) {
1135 while (endpar && endpar->getDepth()) {
1136 endpar = endpar->next();
1137 undoendpar = endpar;
1141 // because of parindents etc.
1142 endpar = endpar->next();
1145 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1148 Paragraph * tmppar = selection.end.par();
1149 while (tmppar != selection.start.par()->previous()) {
1150 setCursor(bview, tmppar, 0);
1151 status(bview, LyXText::NEED_MORE_REFRESH);
1152 refresh_row = cursor.row();
1153 refresh_y = cursor.y() - cursor.row()->baseline();
1154 cursor.par()->params().lineTop(line_top);
1155 cursor.par()->params().lineBottom(line_bottom);
1156 cursor.par()->params().pagebreakTop(pagebreak_top);
1157 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1158 cursor.par()->params().spaceTop(space_top);
1159 cursor.par()->params().spaceBottom(space_bottom);
1160 // does the layout allow the new alignment?
1161 if (align == LYX_ALIGN_LAYOUT)
1162 align = textclasslist
1163 .Style(bview->buffer()->params.textclass,
1164 cursor.par()->getLayout()).align;
1165 if (align & textclasslist
1166 .Style(bview->buffer()->params.textclass,
1167 cursor.par()->getLayout()).alignpossible) {
1168 if (align == textclasslist
1169 .Style(bview->buffer()->params.textclass,
1170 cursor.par()->getLayout()).align)
1171 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1173 cursor.par()->params().align(align);
1175 cursor.par()->setLabelWidthString(labelwidthstring);
1176 cursor.par()->params().noindent(noindent);
1177 tmppar = cursor.par()->previous();
1180 redoParagraphs(bview, selection.start, endpar);
1183 setCursor(bview, selection.start.par(), selection.start.pos());
1184 selection.cursor = cursor;
1185 setCursor(bview, selection.end.par(), selection.end.pos());
1186 setSelection(bview);
1187 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1189 bview->updateInset(inset_owner, true);
1193 char loweralphaCounter(int n)
1195 if (n < 1 || n > 26)
1205 char alphaCounter(int n)
1207 if (n < 1 || n > 26)
1215 char hebrewCounter(int n)
1217 static const char hebrew[22] = {
1218 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1219 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1222 if (n < 1 || n > 22)
1230 string const romanCounter(int n)
1232 static char const * roman[20] = {
1233 "i", "ii", "iii", "iv", "v",
1234 "vi", "vii", "viii", "ix", "x",
1235 "xi", "xii", "xiii", "xiv", "xv",
1236 "xvi", "xvii", "xviii", "xix", "xx"
1238 if (n < 1 || n > 20)
1247 // set the counter of a paragraph. This includes the labels
1248 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1250 LyXLayout const & layout =
1251 textclasslist.Style(buf->params.textclass,
1254 LyXTextClass const & textclass =
1255 textclasslist.TextClass(buf->params.textclass);
1257 // copy the prev-counters to this one,
1258 // unless this is the first paragraph
1259 if (par->previous()) {
1260 for (int i = 0; i < 10; ++i) {
1261 par->setCounter(i, par->previous()->getFirstCounter(i));
1263 par->params().appendix(par->previous()->params().appendix());
1264 if (!par->params().appendix() && par->params().startOfAppendix()) {
1265 par->params().appendix(true);
1266 for (int i = 0; i < 10; ++i) {
1267 par->setCounter(i, 0);
1270 par->enumdepth = par->previous()->enumdepth;
1271 par->itemdepth = par->previous()->itemdepth;
1273 for (int i = 0; i < 10; ++i) {
1274 par->setCounter(i, 0);
1276 par->params().appendix(par->params().startOfAppendix());
1281 /* Maybe we have to increment the enumeration depth.
1282 * BUT, enumeration in a footnote is considered in isolation from its
1283 * surrounding paragraph so don't increment if this is the
1284 * first line of the footnote
1285 * AND, bibliographies can't have their depth changed ie. they
1286 * are always of depth 0
1289 && par->previous()->getDepth() < par->getDepth()
1290 && textclasslist.Style(buf->params.textclass,
1291 par->previous()->getLayout()
1292 ).labeltype == LABEL_COUNTER_ENUMI
1293 && par->enumdepth < 3
1294 && layout.labeltype != LABEL_BIBLIO) {
1298 // Maybe we have to decrement the enumeration depth, see note above
1300 && par->previous()->getDepth() > par->getDepth()
1301 && layout.labeltype != LABEL_BIBLIO) {
1302 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1303 par->setCounter(6 + par->enumdepth,
1304 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1305 /* reset the counters.
1306 * A depth change is like a breaking layout
1308 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1309 par->setCounter(i, 0);
1312 if (!par->params().labelString().empty()) {
1313 par->params().labelString(string());
1316 if (layout.margintype == MARGIN_MANUAL) {
1317 if (par->params().labelWidthString().empty()) {
1318 par->setLabelWidthString(layout.labelstring());
1321 par->setLabelWidthString(string());
1324 // is it a layout that has an automatic label?
1325 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1327 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1328 if (i >= 0 && i<= buf->params.secnumdepth) {
1329 par->incCounter(i); // increment the counter
1331 // Is there a label? Useful for Chapter layout
1332 if (!par->params().appendix()) {
1333 if (!layout.labelstring().empty())
1334 par->params().labelString(layout.labelstring());
1336 par->params().labelString(string());
1338 if (!layout.labelstring_appendix().empty())
1339 par->params().labelString(layout.labelstring_appendix());
1341 par->params().labelString(string());
1346 if (!par->params().appendix()) {
1347 switch (2 * LABEL_COUNTER_CHAPTER -
1348 textclass.maxcounter() + i) {
1349 case LABEL_COUNTER_CHAPTER:
1350 s << par->getCounter(i);
1352 case LABEL_COUNTER_SECTION:
1353 s << par->getCounter(i - 1) << '.'
1354 << par->getCounter(i);
1356 case LABEL_COUNTER_SUBSECTION:
1357 s << par->getCounter(i - 2) << '.'
1358 << par->getCounter(i - 1) << '.'
1359 << par->getCounter(i);
1361 case LABEL_COUNTER_SUBSUBSECTION:
1362 s << par->getCounter(i - 3) << '.'
1363 << par->getCounter(i - 2) << '.'
1364 << par->getCounter(i - 1) << '.'
1365 << par->getCounter(i);
1368 case LABEL_COUNTER_PARAGRAPH:
1369 s << par->getCounter(i - 4) << '.'
1370 << par->getCounter(i - 3) << '.'
1371 << par->getCounter(i - 2) << '.'
1372 << par->getCounter(i - 1) << '.'
1373 << par->getCounter(i);
1375 case LABEL_COUNTER_SUBPARAGRAPH:
1376 s << par->getCounter(i - 5) << '.'
1377 << par->getCounter(i - 4) << '.'
1378 << par->getCounter(i - 3) << '.'
1379 << par->getCounter(i - 2) << '.'
1380 << par->getCounter(i - 1) << '.'
1381 << par->getCounter(i);
1385 // Can this ever be reached? And in the
1386 // case it is, how can this be correct?
1388 s << par->getCounter(i) << '.';
1391 } else { // appendix
1392 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1393 case LABEL_COUNTER_CHAPTER:
1394 if (par->isRightToLeftPar(buf->params))
1395 s << hebrewCounter(par->getCounter(i));
1397 s << alphaCounter(par->getCounter(i));
1399 case LABEL_COUNTER_SECTION:
1400 if (par->isRightToLeftPar(buf->params))
1401 s << hebrewCounter(par->getCounter(i - 1));
1403 s << alphaCounter(par->getCounter(i - 1));
1406 << par->getCounter(i);
1409 case LABEL_COUNTER_SUBSECTION:
1410 if (par->isRightToLeftPar(buf->params))
1411 s << hebrewCounter(par->getCounter(i - 2));
1413 s << alphaCounter(par->getCounter(i - 2));
1416 << par->getCounter(i-1) << '.'
1417 << par->getCounter(i);
1420 case LABEL_COUNTER_SUBSUBSECTION:
1421 if (par->isRightToLeftPar(buf->params))
1422 s << hebrewCounter(par->getCounter(i-3));
1424 s << alphaCounter(par->getCounter(i-3));
1427 << par->getCounter(i-2) << '.'
1428 << par->getCounter(i-1) << '.'
1429 << par->getCounter(i);
1432 case LABEL_COUNTER_PARAGRAPH:
1433 if (par->isRightToLeftPar(buf->params))
1434 s << hebrewCounter(par->getCounter(i-4));
1436 s << alphaCounter(par->getCounter(i-4));
1439 << par->getCounter(i-3) << '.'
1440 << par->getCounter(i-2) << '.'
1441 << par->getCounter(i-1) << '.'
1442 << par->getCounter(i);
1445 case LABEL_COUNTER_SUBPARAGRAPH:
1446 if (par->isRightToLeftPar(buf->params))
1447 s << hebrewCounter(par->getCounter(i-5));
1449 s << alphaCounter(par->getCounter(i-5));
1452 << par->getCounter(i-4) << '.'
1453 << par->getCounter(i-3) << '.'
1454 << par->getCounter(i-2) << '.'
1455 << par->getCounter(i-1) << '.'
1456 << par->getCounter(i);
1460 // Can this ever be reached? And in the
1461 // case it is, how can this be correct?
1463 s << par->getCounter(i) << '.';
1469 par->params().labelString(par->params().labelString() +s.str().c_str());
1470 // We really want to remove the c_str as soon as
1473 for (i++; i < 10; ++i) {
1474 // reset the following counters
1475 par->setCounter(i, 0);
1477 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1478 for (i++; i < 10; ++i) {
1479 // reset the following counters
1480 par->setCounter(i, 0);
1482 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1483 par->incCounter(i + par->enumdepth);
1484 int number = par->getCounter(i + par->enumdepth);
1488 switch (par->enumdepth) {
1490 if (par->isRightToLeftPar(buf->params))
1492 << hebrewCounter(number)
1496 << loweralphaCounter(number)
1500 if (par->isRightToLeftPar(buf->params))
1501 s << '.' << romanCounter(number);
1503 s << romanCounter(number) << '.';
1506 if (par->isRightToLeftPar(buf->params))
1508 << alphaCounter(number);
1510 s << alphaCounter(number)
1514 if (par->isRightToLeftPar(buf->params))
1521 par->params().labelString(s.str().c_str());
1523 for (i += par->enumdepth + 1; i < 10; ++i) {
1524 // reset the following counters
1525 par->setCounter(i, 0);
1529 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1530 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1532 int number = par->getCounter(i);
1534 InsetCommandParams p( "bibitem" );
1535 par->bibkey = new InsetBibKey(p);
1537 par->bibkey->setCounter(number);
1538 par->params().labelString(layout.labelstring());
1540 // In biblio should't be following counters but...
1542 string s = layout.labelstring();
1544 // the caption hack:
1545 if (layout.labeltype == LABEL_SENSITIVE) {
1546 bool isOK (par->inInset() && par->inInset()->owner() &&
1547 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1550 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1552 = floatList.getType(tmp->type());
1553 // We should get the correct number here too.
1554 s = fl.name() + " #:";
1556 /* par->SetLayout(0);
1557 s = layout->labelstring; */
1558 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1559 ? " :úåòîùî øñç" : "Senseless: ";
1562 par->params().labelString(s);
1564 /* reset the enumeration counter. They are always resetted
1565 * when there is any other layout between */
1566 for (int i = 6 + par->enumdepth; i < 10; ++i)
1567 par->setCounter(i, 0);
1572 // Updates all counters BEHIND the row. Changed paragraphs
1573 // with a dynamic left margin will be rebroken.
1574 void LyXText::updateCounters(BufferView * bview, Row * row) const
1582 par = row->par()->next();
1586 while (row->par() != par)
1589 setCounter(bview->buffer(), par);
1591 // now check for the headline layouts. remember that they
1592 // have a dynamic left margin
1593 if ((textclasslist.Style(bview->buffer()->params.textclass,
1594 par->layout).margintype == MARGIN_DYNAMIC
1595 || textclasslist.Style(bview->buffer()->params.textclass,
1596 par->layout).labeltype == LABEL_SENSITIVE)) {
1598 // Rebreak the paragraph
1599 removeParagraph(row);
1600 appendParagraph(bview, row);
1607 void LyXText::insertInset(BufferView * bview, Inset * inset)
1609 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1611 setUndo(bview, Undo::INSERT,
1612 cursor.par(), cursor.par()->next());
1613 cursor.par()->insertInset(cursor.pos(), inset);
1614 // Just to rebreak and refresh correctly.
1615 // The character will not be inserted a second time
1616 insertChar(bview, Paragraph::META_INSET);
1618 // If we enter a highly editable inset the cursor should be to before
1619 // the inset. This couldn't happen before as Undo was not handled inside
1620 // inset now after the Undo LyX tries to call inset->Edit(...) again
1621 // and cannot do this as the cursor is behind the inset and GetInset
1622 // does not return the inset!
1623 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1624 cursorLeft(bview, true);
1630 void LyXText::copyEnvironmentType()
1632 copylayouttype = cursor.par()->getLayout();
1636 void LyXText::pasteEnvironmentType(BufferView * bview)
1638 setLayout(bview, copylayouttype);
1642 void LyXText::cutSelection(BufferView * bview, bool doclear)
1644 // Stuff what we got on the clipboard. Even if there is no selection.
1646 // There is a problem with having the stuffing here in that the
1647 // larger the selection the slower LyX will get. This can be
1648 // solved by running the line below only when the selection has
1649 // finished. The solution used currently just works, to make it
1650 // faster we need to be more clever and probably also have more
1651 // calls to stuffClipboard. (Lgb)
1652 bview->stuffClipboard(selectionAsString(bview->buffer()));
1654 // This doesn't make sense, if there is no selection
1655 if (!selection.set())
1658 // OK, we have a selection. This is always between selection.start
1659 // and selection.end
1661 // make sure that the depth behind the selection are restored, too
1662 Paragraph * endpar = selection.end.par()->next();
1663 Paragraph * undoendpar = endpar;
1665 if (endpar && endpar->getDepth()) {
1666 while (endpar && endpar->getDepth()) {
1667 endpar = endpar->next();
1668 undoendpar = endpar;
1670 } else if (endpar) {
1671 endpar = endpar->next(); // because of parindents etc.
1674 setUndo(bview, Undo::DELETE,
1675 selection.start.par(), undoendpar);
1677 // there are two cases: cut only within one paragraph or
1678 // more than one paragraph
1679 if (selection.start.par() == selection.end.par()) {
1680 // only within one paragraph
1681 endpar = selection.end.par();
1682 int pos = selection.end.pos();
1683 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1684 selection.start.pos(), pos,
1685 bview->buffer()->params.textclass, doclear);
1686 selection.end.pos(pos);
1688 endpar = selection.end.par();
1689 int pos = selection.end.pos();
1690 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1691 selection.start.pos(), pos,
1692 bview->buffer()->params.textclass, doclear);
1694 selection.end.par(endpar);
1695 selection.end.pos(pos);
1696 cursor.pos(selection.end.pos());
1698 endpar = endpar->next();
1700 // sometimes necessary
1702 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1704 redoParagraphs(bview, selection.start, endpar);
1706 // cutSelection can invalidate the cursor so we need to set
1708 cursor = selection.start;
1710 // need a valid cursor. (Lgb)
1713 setCursor(bview, cursor.par(), cursor.pos());
1714 selection.cursor = cursor;
1715 updateCounters(bview, cursor.row());
1719 void LyXText::copySelection(BufferView * bview)
1721 // Stuff what we got on the clipboard. Even if there is no selection.
1723 // There is a problem with having the stuffing here in that the
1724 // larger the selection the slower LyX will get. This can be
1725 // solved by running the line below only when the selection has
1726 // finished. The solution used currently just works, to make it
1727 // faster we need to be more clever and probably also have more
1728 // calls to stuffClipboard. (Lgb)
1729 bview->stuffClipboard(selectionAsString(bview->buffer()));
1731 // this doesnt make sense, if there is no selection
1732 if (!selection.set())
1735 // ok we have a selection. This is always between selection.start
1736 // and sel_end cursor
1738 // copy behind a space if there is one
1739 while (selection.start.par()->size() > selection.start.pos()
1740 && selection.start.par()->isLineSeparator(selection.start.pos())
1741 && (selection.start.par() != selection.end.par()
1742 || selection.start.pos() < selection.end.pos()))
1743 selection.start.pos(selection.start.pos() + 1);
1745 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1746 selection.start.pos(), selection.end.pos(),
1747 bview->buffer()->params.textclass);
1751 void LyXText::pasteSelection(BufferView * bview)
1753 // this does not make sense, if there is nothing to paste
1754 if (!CutAndPaste::checkPastePossible(cursor.par()))
1757 setUndo(bview, Undo::INSERT,
1758 cursor.par(), cursor.par()->next());
1761 Paragraph * actpar = cursor.par();
1763 int pos = cursor.pos();
1764 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1765 bview->buffer()->params.textclass);
1767 redoParagraphs(bview, cursor, endpar);
1769 setCursor(bview, cursor.par(), cursor.pos());
1772 selection.cursor = cursor;
1773 setCursor(bview, actpar, pos);
1774 setSelection(bview);
1775 updateCounters(bview, cursor.row());
1779 // returns a pointer to the very first Paragraph
1780 Paragraph * LyXText::firstParagraph() const
1782 return ownerParagraph();
1786 // sets the selection over the number of characters of string, no check!!
1787 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1792 selection.cursor = cursor;
1793 for (string::size_type i = 0; i < str.length(); ++i)
1795 setSelection(bview);
1799 // simple replacing. The font of the first selected character is used
1800 void LyXText::replaceSelectionWithString(BufferView * bview,
1803 setCursorParUndo(bview);
1806 if (!selection.set()) { // create a dummy selection
1807 selection.end = cursor;
1808 selection.start = cursor;
1811 // Get font setting before we cut
1812 Paragraph::size_type pos = selection.end.pos();
1813 LyXFont const font = selection.start.par()
1814 ->getFontSettings(bview->buffer()->params,
1815 selection.start.pos());
1817 // Insert the new string
1818 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1819 selection.end.par()->insertChar(pos, (*cit), font);
1823 // Cut the selection
1824 cutSelection(bview);
1830 // needed to insert the selection
1831 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1833 Paragraph * par = cursor.par();
1834 Paragraph::size_type pos = cursor.pos();
1835 Paragraph * endpar = cursor.par()->next();
1837 setCursorParUndo(bview);
1839 // only to be sure, should not be neccessary
1842 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1844 redoParagraphs(bview, cursor, endpar);
1845 setCursor(bview, cursor.par(), cursor.pos());
1846 selection.cursor = cursor;
1847 setCursor(bview, par, pos);
1848 setSelection(bview);
1852 // turns double-CR to single CR, others where converted into one
1853 // blank. Then InsertStringAsLines is called
1854 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1856 string linestr(str);
1857 bool newline_inserted = false;
1858 for (string::size_type i = 0; i < linestr.length(); ++i) {
1859 if (linestr[i] == '\n') {
1860 if (newline_inserted) {
1861 // we know that \r will be ignored by
1862 // InsertStringA. Of course, it is a dirty
1863 // trick, but it works...
1864 linestr[i - 1] = '\r';
1868 newline_inserted = true;
1870 } else if (IsPrintable(linestr[i])) {
1871 newline_inserted = false;
1874 insertStringAsLines(bview, linestr);
1878 bool LyXText::gotoNextInset(BufferView * bview,
1879 std::vector<Inset::Code> const & codes,
1880 string const & contents) const
1882 LyXCursor res = cursor;
1885 if (res.pos() < res.par()->size() - 1) {
1886 res.pos(res.pos() + 1);
1888 res.par(res.par()->next());
1892 } while (res.par() &&
1893 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1894 && (inset = res.par()->getInset(res.pos())) != 0
1895 && find(codes.begin(), codes.end(), inset->lyxCode())
1897 && (contents.empty() ||
1898 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1902 setCursor(bview, res.par(), res.pos());
1909 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1910 Paragraph::size_type pos)
1912 LyXCursor tmpcursor;
1915 Paragraph::size_type z;
1916 Row * row = getRow(par, pos, y);
1918 // is there a break one row above
1919 if (row->previous() && row->previous()->par() == row->par()) {
1920 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1921 if (z >= row->pos()) {
1922 // set the dimensions of the row above
1923 y -= row->previous()->height();
1925 refresh_row = row->previous();
1926 status(bview, LyXText::NEED_MORE_REFRESH);
1928 breakAgain(bview, row->previous());
1930 // set the cursor again. Otherwise
1931 // dangling pointers are possible
1932 setCursor(bview, cursor.par(), cursor.pos(),
1933 false, cursor.boundary());
1934 selection.cursor = cursor;
1939 int const tmpheight = row->height();
1940 Paragraph::size_type const tmplast = rowLast(row);
1944 breakAgain(bview, row);
1945 if (row->height() == tmpheight && rowLast(row) == tmplast)
1946 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1948 status(bview, LyXText::NEED_MORE_REFRESH);
1950 // check the special right address boxes
1951 if (textclasslist.Style(bview->buffer()->params.textclass,
1952 par->getLayout()).margintype
1953 == MARGIN_RIGHT_ADDRESS_BOX) {
1960 redoDrawingOfParagraph(bview, tmpcursor);
1963 // set the cursor again. Otherwise dangling pointers are possible
1964 // also set the selection
1966 if (selection.set()) {
1968 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1969 false, selection.cursor.boundary());
1970 selection.cursor = cursor;
1971 setCursorIntern(bview, selection.start.par(),
1972 selection.start.pos(),
1973 false, selection.start.boundary());
1974 selection.start = cursor;
1975 setCursorIntern(bview, selection.end.par(),
1976 selection.end.pos(),
1977 false, selection.end.boundary());
1978 selection.end = cursor;
1979 setCursorIntern(bview, last_sel_cursor.par(),
1980 last_sel_cursor.pos(),
1981 false, last_sel_cursor.boundary());
1982 last_sel_cursor = cursor;
1985 setCursorIntern(bview, cursor.par(), cursor.pos(),
1986 false, cursor.boundary());
1990 // returns false if inset wasn't found
1991 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1993 // first check the current paragraph
1994 int pos = cursor.par()->getPositionOfInset(inset);
1996 checkParagraph(bview, cursor.par(), pos);
2000 // check every paragraph
2002 Paragraph * par = firstParagraph();
2004 pos = par->getPositionOfInset(inset);
2006 checkParagraph(bview, par, pos);
2016 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2017 Paragraph::size_type pos,
2018 bool setfont, bool boundary) const
2020 LyXCursor old_cursor = cursor;
2021 setCursorIntern(bview, par, pos, setfont, boundary);
2022 deleteEmptyParagraphMechanism(bview, old_cursor);
2026 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2027 Paragraph::size_type pos, bool boundary) const
2031 cur.boundary(boundary);
2033 // get the cursor y position in text
2035 Row * row = getRow(par, pos, y);
2036 // y is now the beginning of the cursor row
2037 y += row->baseline();
2038 // y is now the cursor baseline
2041 // now get the cursors x position
2043 float fill_separator;
2045 float fill_label_hfill;
2046 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2048 Paragraph::size_type cursor_vpos = 0;
2049 Paragraph::size_type last = rowLastPrintable(row);
2051 if (pos > last + 1) // This shouldn't happen.
2053 else if (pos < row->pos())
2056 if (last < row->pos())
2057 cursor_vpos = row->pos();
2058 else if (pos > last && !boundary)
2059 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2060 ? row->pos() : last + 1;
2061 else if (pos > row->pos() &&
2062 (pos > last || boundary))
2063 /// Place cursor after char at (logical) position pos - 1
2064 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2065 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2067 /// Place cursor before char at (logical) position pos
2068 cursor_vpos = (bidi_level(pos) % 2 == 0)
2069 ? log2vis(pos) : log2vis(pos) + 1;
2071 Paragraph::size_type main_body =
2072 beginningOfMainBody(bview->buffer(), row->par());
2073 if ((main_body > 0) &&
2074 ((main_body-1 > last) ||
2075 !row->par()->isLineSeparator(main_body-1)))
2078 for (Paragraph::size_type vpos = row->pos();
2079 vpos < cursor_vpos; ++vpos) {
2080 pos = vis2log(vpos);
2081 if (main_body > 0 && pos == main_body - 1) {
2082 x += fill_label_hfill +
2083 lyxfont::width(textclasslist.Style(
2084 bview->buffer()->params.textclass,
2085 row->par()->getLayout())
2087 getFont(bview->buffer(), row->par(), -2));
2088 if (row->par()->isLineSeparator(main_body-1))
2089 x -= singleWidth(bview, row->par(),main_body-1);
2091 if (hfillExpansion(bview->buffer(), row, pos)) {
2092 x += singleWidth(bview, row->par(), pos);
2093 if (pos >= main_body)
2096 x += fill_label_hfill;
2097 } else if (row->par()->isSeparator(pos)) {
2098 x += singleWidth(bview, row->par(), pos);
2099 if (pos >= main_body)
2100 x += fill_separator;
2102 x += singleWidth(bview, row->par(), pos);
2111 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2112 Paragraph::size_type pos,
2113 bool setfont, bool boundary) const
2115 InsetText * it = static_cast<InsetText *>(par->inInset());
2116 if (it && (it != inset_owner)) {
2117 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2120 setCursor(bview, cursor, par, pos, boundary);
2122 setCurrentFont(bview);
2127 void LyXText::setCurrentFont(BufferView * bview) const
2129 Paragraph::size_type pos = cursor.pos();
2130 if (cursor.boundary() && pos > 0)
2134 if (pos == cursor.par()->size())
2136 else // potentional bug... BUG (Lgb)
2137 if (cursor.par()->isSeparator(pos)) {
2138 if (pos > cursor.row()->pos() &&
2139 bidi_level(pos) % 2 ==
2140 bidi_level(pos - 1) % 2)
2142 else if (pos + 1 < cursor.par()->size())
2148 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2149 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2151 if (cursor.pos() == cursor.par()->size() &&
2152 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2153 !cursor.boundary()) {
2154 Language const * lang =
2155 cursor.par()->getParLanguage(bview->buffer()->params);
2156 current_font.setLanguage(lang);
2157 current_font.setNumber(LyXFont::OFF);
2158 real_current_font.setLanguage(lang);
2159 real_current_font.setNumber(LyXFont::OFF);
2164 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2166 LyXCursor old_cursor = cursor;
2169 // Get the row first.
2171 Row * row = getRowNearY(y);
2174 int column = getColumnNearX(bview, row, x, bound);
2176 cursor.par(row->par());
2177 cursor.pos(row->pos() + column);
2179 cursor.y(y + row->baseline());
2181 cursor.boundary(bound);
2183 setCursorFromCoordinates(bview, cursor, x, y);
2185 setCurrentFont(bview);
2186 deleteEmptyParagraphMechanism(bview, old_cursor);
2190 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2193 // Get the row first.
2195 Row * row = getRowNearY(y);
2197 int const column = getColumnNearX(bview, row, x, bound);
2199 cur.par(row->par());
2200 cur.pos(row->pos() + column);
2202 cur.y(y + row->baseline());
2204 cur.boundary(bound);
2208 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2210 if (cursor.pos() > 0) {
2211 bool boundary = cursor.boundary();
2212 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2213 if (!internal && !boundary &&
2214 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2215 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2216 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2217 Paragraph * par = cursor.par()->previous();
2218 setCursor(bview, par, par->size());
2223 void LyXText::cursorRight(BufferView * bview, bool internal) const
2225 if (!internal && cursor.boundary() &&
2226 !cursor.par()->isNewline(cursor.pos()))
2227 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2228 else if (cursor.pos() < cursor.par()->size()) {
2229 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2231 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2232 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2233 } else if (cursor.par()->next())
2234 setCursor(bview, cursor.par()->next(), 0);
2238 void LyXText::cursorUp(BufferView * bview) const
2240 setCursorFromCoordinates(bview, cursor.x_fix(),
2241 cursor.y() - cursor.row()->baseline() - 1);
2245 void LyXText::cursorDown(BufferView * bview) const
2247 setCursorFromCoordinates(bview, cursor.x_fix(),
2248 cursor.y() - cursor.row()->baseline()
2249 + cursor.row()->height() + 1);
2253 void LyXText::cursorUpParagraph(BufferView * bview) const
2255 if (cursor.pos() > 0) {
2256 setCursor(bview, cursor.par(), 0);
2258 else if (cursor.par()->previous()) {
2259 setCursor(bview, cursor.par()->previous(), 0);
2264 void LyXText::cursorDownParagraph(BufferView * bview) const
2266 if (cursor.par()->next()) {
2267 setCursor(bview, cursor.par()->next(), 0);
2269 setCursor(bview, cursor.par(), cursor.par()->size());
2274 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2275 LyXCursor const & old_cursor) const
2277 // Would be wrong to delete anything if we have a selection.
2278 if (selection.set()) return;
2280 // We allow all kinds of "mumbo-jumbo" when freespacing.
2281 if (textclasslist.Style(bview->buffer()->params.textclass,
2282 old_cursor.par()->getLayout()).free_spacing)
2285 bool deleted = false;
2287 /* Ok I'll put some comments here about what is missing.
2288 I have fixed BackSpace (and thus Delete) to not delete
2289 double-spaces automagically. I have also changed Cut,
2290 Copy and Paste to hopefully do some sensible things.
2291 There are still some small problems that can lead to
2292 double spaces stored in the document file or space at
2293 the beginning of paragraphs. This happens if you have
2294 the cursor betwenn to spaces and then save. Or if you
2295 cut and paste and the selection have a space at the
2296 beginning and then save right after the paste. I am
2297 sure none of these are very hard to fix, but I will
2298 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2299 that I can get some feedback. (Lgb)
2302 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2303 // delete the LineSeparator.
2306 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2307 // delete the LineSeparator.
2310 // If the pos around the old_cursor were spaces, delete one of them.
2311 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2312 // Only if the cursor has really moved
2314 if (old_cursor.pos() > 0
2315 && old_cursor.pos() < old_cursor.par()->size()
2316 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2317 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2318 old_cursor.par()->erase(old_cursor.pos() - 1);
2319 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2321 if (old_cursor.par() == cursor.par() &&
2322 cursor.pos() > old_cursor.pos()) {
2323 setCursorIntern(bview, cursor.par(),
2326 setCursorIntern(bview, cursor.par(),
2332 // Do not delete empty paragraphs with keepempty set.
2333 if ((textclasslist.Style(bview->buffer()->params.textclass,
2334 old_cursor.par()->getLayout())).keepempty)
2337 LyXCursor tmpcursor;
2339 if (old_cursor.par() != cursor.par()) {
2340 if ((old_cursor.par()->size() == 0
2341 || (old_cursor.par()->size() == 1
2342 && old_cursor.par()->isLineSeparator(0)))) {
2343 // ok, we will delete anything
2345 // make sure that you do not delete any environments
2346 status(bview, LyXText::NEED_MORE_REFRESH);
2349 if (old_cursor.row()->previous()) {
2350 refresh_row = old_cursor.row()->previous();
2351 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2353 cursor = old_cursor; // that undo can restore the right cursor position
2354 Paragraph * endpar = old_cursor.par()->next();
2355 if (endpar && endpar->getDepth()) {
2356 while (endpar && endpar->getDepth()) {
2357 endpar = endpar->next();
2360 setUndo(bview, Undo::DELETE,
2366 removeRow(old_cursor.row());
2367 if (ownerParagraph() == old_cursor.par()) {
2368 ownerParagraph(ownerParagraph()->next());
2371 delete old_cursor.par();
2373 /* Breakagain the next par. Needed
2374 * because of the parindent that
2375 * can occur or dissappear. The
2376 * next row can change its height,
2377 * if there is another layout before */
2378 if (refresh_row->next()) {
2379 breakAgain(bview, refresh_row->next());
2380 updateCounters(bview, refresh_row);
2382 setHeightOfRow(bview, refresh_row);
2384 refresh_row = old_cursor.row()->next();
2385 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2388 cursor = old_cursor; // that undo can restore the right cursor position
2389 Paragraph * endpar = old_cursor.par()->next();
2390 if (endpar && endpar->getDepth()) {
2391 while (endpar && endpar->getDepth()) {
2392 endpar = endpar->next();
2395 setUndo(bview, Undo::DELETE,
2401 removeRow(old_cursor.row());
2403 if (ownerParagraph() == old_cursor.par()) {
2404 ownerParagraph(ownerParagraph()->next());
2407 delete old_cursor.par();
2409 /* Breakagain the next par. Needed
2410 because of the parindent that can
2411 occur or dissappear.
2412 The next row can change its height,
2413 if there is another layout before
2416 breakAgain(bview, refresh_row);
2417 updateCounters(bview, refresh_row->previous());
2423 setCursorIntern(bview, cursor.par(), cursor.pos());
2425 if (selection.cursor.par() == old_cursor.par()
2426 && selection.cursor.pos() == selection.cursor.pos()) {
2427 // correct selection
2428 selection.cursor = cursor;
2432 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2433 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2435 setCursorIntern(bview, cursor.par(), cursor.pos());
2436 selection.cursor = cursor;
2443 void LyXText::toggleAppendix(BufferView * bview)
2445 Paragraph * par = cursor.par();
2446 bool start = !par->params().startOfAppendix();
2448 // ensure that we have only one start_of_appendix in this document
2449 Paragraph * tmp = firstParagraph();
2450 for (; tmp; tmp = tmp->next()) {
2451 tmp->params().startOfAppendix(false);
2454 par->params().startOfAppendix(start);
2456 // we can set the refreshing parameters now
2457 status(bview, LyXText::NEED_MORE_REFRESH);
2459 refresh_row = 0; // not needed for full update
2460 updateCounters(bview, 0);
2461 setCursor(bview, cursor.par(), cursor.pos());
2465 Paragraph * LyXText::ownerParagraph() const
2468 return inset_owner->paragraph();
2470 return bv_owner->buffer()->paragraph;
2474 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2477 inset_owner->paragraph(p);
2479 bv_owner->buffer()->paragraph = p;
2484 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2486 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2487 if (op && op->inInset()) {
2488 static_cast<InsetText *>(op->inInset())->paragraph(p);
2491 inset_owner->paragraph(p);
2493 bv_owner->buffer()->paragraph = p;
2500 LyXText::text_status LyXText::status() const
2506 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2509 if ((status_ != NEED_MORE_REFRESH)
2510 || (status_ == NEED_MORE_REFRESH)
2511 && (st != NEED_VERY_LITTLE_REFRESH)) {
2513 if (inset_owner && st != UNCHANGED) {
2514 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2518 #warning Please tell what the intention is here. (Lgb)
2519 // The above does not make any sense, I changed it to what is here,
2520 // but it still does not make much sense. (Lgb)
2521 #warning Sure have a look now! (Jug)
2522 // well as much as I know && binds more then || so the above and the
2523 // below are identical (this for your known use of parentesis!)
2524 // Now some explanation:
2525 // We should only go up with refreshing code so this means that if
2526 // we have a MORE refresh we should never set it to LITTLE if we still
2527 // didn't handle it (and then it will be UNCHANGED. Now as long as
2528 // we stay inside one LyXText this may work but we need to tell the
2529 // outermost LyXText that it should REALLY draw us if there is some
2530 // change in a Inset::LyXText. So you see that when we are inside a
2531 // inset's LyXText we give the LITTLE to the outermost LyXText to
2532 // tell'em that it should redraw the actual row (where the inset
2533 // resides! Capito?!
2535 if ((status_ != NEED_MORE_REFRESH)
2536 || (status_ == NEED_MORE_REFRESH
2537 && st != NEED_VERY_LITTLE_REFRESH))
2540 if (inset_owner && st != UNCHANGED) {
2541 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2542 if (!bview->text->refresh_row) {
2543 bview->text->refresh_row = bview->text->cursor.row();
2544 bview->text->refresh_y = bview->text->cursor.y() -
2545 bview->text->cursor.row()->baseline();