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(),
679 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
681 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
682 // Update current font
683 real_current_font.update(font, toggleall);
685 // Reduce to implicit settings
686 current_font = real_current_font;
687 current_font.reduce(layoutfont);
688 // And resolve it completely
689 real_current_font.realize(layoutfont,
690 bview->buffer()->params.language);
694 LyXCursor tmpcursor = cursor; // store the current cursor
696 // ok we have a selection. This is always between sel_start_cursor
697 // and sel_end cursor
699 setUndo(bview, Undo::EDIT,
700 selection.start.par(),
701 selection.end.par()->next());
703 cursor = selection.start;
704 while (cursor.par() != selection.end.par() ||
705 (cursor.pos() < selection.end.pos())) {
706 if (cursor.pos() < cursor.par()->size()) {
707 // an open footnote should behave
709 setCharFont(bview, cursor.par(), cursor.pos(),
711 cursor.pos(cursor.pos() + 1);
714 cursor.par(cursor.par()->next());
719 redoParagraphs(bview, selection.start, selection.end.par()->next());
721 // we have to reset the selection, because the
722 // geometry could have changed
723 setCursor(bview, selection.start.par(), selection.start.pos());
724 selection.cursor = cursor;
725 setCursor(bview, selection.end.par(), selection.end.pos());
728 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
729 tmpcursor.boundary());
733 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
735 Row * tmprow = cur.row();
736 int y = cur.y() - tmprow->baseline();
738 setHeightOfRow(bview, tmprow);
740 while (tmprow->previous()
741 && tmprow->previous()->par() == tmprow->par()) {
742 tmprow = tmprow->previous();
743 y -= tmprow->height();
744 setHeightOfRow(bview, tmprow);
747 // we can set the refreshing parameters now
748 status(bview, LyXText::NEED_MORE_REFRESH);
750 refresh_row = tmprow;
751 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
755 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
757 Row * tmprow = cur.row();
759 int y = cur.y() - tmprow->baseline();
760 setHeightOfRow(bview, tmprow);
762 while (tmprow->previous()
763 && tmprow->previous()->par() == tmprow->par()) {
764 tmprow = tmprow->previous();
765 y -= tmprow->height();
768 // we can set the refreshing parameters now
769 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
771 refresh_row = tmprow;
773 status(bview, LyXText::NEED_MORE_REFRESH);
774 setCursor(bview, cur.par(), cur.pos());
778 // deletes and inserts again all paragaphs between the cursor
779 // and the specified par
780 // This function is needed after SetLayout and SetFont etc.
781 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
782 Paragraph const * endpar) const
785 Paragraph * tmppar = 0;
786 Paragraph * first_phys_par = 0;
788 Row * tmprow = cur.row();
790 int y = cur.y() - tmprow->baseline();
792 if (!tmprow->previous()) {
793 // a trick/hack for UNDO
794 // Can somebody please tell me _why_ this solves
796 first_phys_par = firstParagraph();
798 first_phys_par = tmprow->par();
799 while (tmprow->previous()
800 && tmprow->previous()->par() == first_phys_par) {
801 tmprow = tmprow->previous();
802 y -= tmprow->height();
806 // we can set the refreshing parameters now
807 status(bview, LyXText::NEED_MORE_REFRESH);
809 refresh_row = tmprow->previous(); /* the real refresh row will
810 be deleted, so I store
814 tmppar = tmprow->next()->par();
817 while (tmppar != endpar) {
818 removeRow(tmprow->next());
820 tmppar = tmprow->next()->par();
825 // remove the first one
826 tmprow2 = tmprow; /* this is because tmprow->previous()
828 tmprow = tmprow->previous();
831 tmppar = first_phys_par;
835 insertParagraph(bview, tmppar, tmprow);
839 while (tmprow->next()
840 && tmprow->next()->par() == tmppar) {
841 tmprow = tmprow->next();
843 tmppar = tmppar->next();
845 } while (tmppar && tmppar != endpar);
847 // this is because of layout changes
849 refresh_y -= refresh_row->height();
850 setHeightOfRow(bview, refresh_row);
852 refresh_row = firstrow;
854 setHeightOfRow(bview, refresh_row);
857 if (tmprow && tmprow->next())
858 setHeightOfRow(bview, tmprow->next());
862 bool LyXText::fullRebreak(BufferView * bview)
868 if (need_break_row) {
869 breakAgain(bview, need_break_row);
877 // important for the screen
880 /* the cursor set functions have a special mechanism. When they
881 * realize, that you left an empty paragraph, they will delete it.
882 * They also delete the corresponding row */
884 // need the selection cursor:
885 void LyXText::setSelection(BufferView * bview)
887 bool const lsel = selection.set();
889 if (!selection.set()) {
890 last_sel_cursor = selection.cursor;
891 selection.start = selection.cursor;
892 selection.end = selection.cursor;
897 // first the toggling area
898 if (cursor.y() < last_sel_cursor.y()
899 || (cursor.y() == last_sel_cursor.y()
900 && cursor.x() < last_sel_cursor.x())) {
901 toggle_end_cursor = last_sel_cursor;
902 toggle_cursor = cursor;
904 toggle_end_cursor = cursor;
905 toggle_cursor = last_sel_cursor;
908 last_sel_cursor = cursor;
910 // and now the whole selection
912 if (selection.cursor.par() == cursor.par())
913 if (selection.cursor.pos() < cursor.pos()) {
914 selection.end = cursor;
915 selection.start = selection.cursor;
917 selection.end = selection.cursor;
918 selection.start = cursor;
920 else if (selection.cursor.y() < cursor.y() ||
921 (selection.cursor.y() == cursor.y()
922 && selection.cursor.x() < cursor.x())) {
923 selection.end = cursor;
924 selection.start = selection.cursor;
927 selection.end = selection.cursor;
928 selection.start = cursor;
931 // a selection with no contents is not a selection
932 if (selection.start.par() == selection.end.par() &&
933 selection.start.pos() == selection.end.pos())
934 selection.set(false);
936 if (inset_owner && (selection.set() || lsel))
937 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
941 string const LyXText::selectionAsString(Buffer const * buffer) const
943 if (!selection.set()) return string();
946 // Special handling if the whole selection is within one paragraph
947 if (selection.start.par() == selection.end.par()) {
948 result += selection.start.par()->asString(buffer,
949 selection.start.pos(),
950 selection.end.pos());
954 // The selection spans more than one paragraph
956 // First paragraph in selection
957 result += selection.start.par()->asString(buffer,
958 selection.start.pos(),
959 selection.start.par()->size())
962 // The paragraphs in between (if any)
963 LyXCursor tmpcur(selection.start);
964 tmpcur.par(tmpcur.par()->next());
965 while (tmpcur.par() != selection.end.par()) {
966 result += tmpcur.par()->asString(buffer, 0,
967 tmpcur.par()->size()) +"\n\n";
968 tmpcur.par(tmpcur.par()->next());
971 // Last paragraph in selection
972 result += selection.end.par()->asString(buffer, 0,
973 selection.end.pos());
979 void LyXText::clearSelection() const
981 selection.set(false);
982 selection.mark(false);
983 selection.end = selection.start = selection.cursor = cursor;
987 void LyXText::cursorHome(BufferView * bview) const
989 setCursor(bview, cursor.par(), cursor.row()->pos());
993 void LyXText::cursorEnd(BufferView * bview) const
995 if (!cursor.row()->next()
996 || cursor.row()->next()->par() != cursor.row()->par()) {
997 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
999 if (cursor.par()->size() &&
1000 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1001 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1002 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1004 setCursor(bview,cursor.par(),
1005 rowLast(cursor.row()) + 1);
1011 void LyXText::cursorTop(BufferView * bview) const
1013 while (cursor.par()->previous())
1014 cursor.par(cursor.par()->previous());
1015 setCursor(bview, cursor.par(), 0);
1019 void LyXText::cursorBottom(BufferView * bview) const
1021 while (cursor.par()->next())
1022 cursor.par(cursor.par()->next());
1023 setCursor(bview, cursor.par(), cursor.par()->size());
1027 void LyXText::toggleFree(BufferView * bview,
1028 LyXFont const & font, bool toggleall)
1030 // If the mask is completely neutral, tell user
1031 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1032 // Could only happen with user style
1033 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1037 // Try implicit word selection
1038 // If there is a change in the language the implicit word selection
1040 LyXCursor resetCursor = cursor;
1041 bool implicitSelection = (font.language() == ignore_language
1042 && font.number() == LyXFont::IGNORE)
1043 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1046 setFont(bview, font, toggleall);
1048 // Implicit selections are cleared afterwards
1049 //and cursor is set to the original position.
1050 if (implicitSelection) {
1052 cursor = resetCursor;
1053 setCursor(bview, cursor.par(), cursor.pos());
1054 selection.cursor = cursor;
1057 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1062 LyXText::getStringToIndex(BufferView * bview)
1066 // Try implicit word selection
1067 // If there is a change in the language the implicit word selection
1069 LyXCursor resetCursor = cursor;
1070 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1072 if (!selection.set()) {
1073 bview->owner()->message(_("Nothing to index!"));
1076 if (selection.start.par() != selection.end.par()) {
1077 bview->owner()->message(_("Cannot index more than one paragraph!"));
1081 idxstring = selectionAsString(bview->buffer());
1083 // Implicit selections are cleared afterwards
1084 //and cursor is set to the original position.
1085 if (implicitSelection) {
1087 cursor = resetCursor;
1088 setCursor(bview, cursor.par(), cursor.pos());
1089 selection.cursor = cursor;
1094 Paragraph::size_type
1095 LyXText::beginningOfMainBody(Buffer const * buf,
1096 Paragraph const * par) const
1098 if (textclasslist.Style(buf->params.textclass,
1099 par->getLayout()).labeltype != LABEL_MANUAL)
1102 return par->beginningOfMainBody();
1106 /* the DTP switches for paragraphs. LyX will store them in the
1107 * first physicla paragraph. When a paragraph is broken, the top settings
1108 * rest, the bottom settings are given to the new one. So I can make shure,
1109 * they do not duplicate themself and you cannnot make dirty things with
1112 void LyXText::setParagraph(BufferView * bview,
1113 bool line_top, bool line_bottom,
1114 bool pagebreak_top, bool pagebreak_bottom,
1115 VSpace const & space_top,
1116 VSpace const & space_bottom,
1118 string labelwidthstring,
1121 LyXCursor tmpcursor = cursor;
1122 if (!selection.set()) {
1123 selection.start = cursor;
1124 selection.end = cursor;
1127 // make sure that the depth behind the selection are restored, too
1128 Paragraph * endpar = selection.end.par()->next();
1129 Paragraph * undoendpar = endpar;
1131 if (endpar && endpar->getDepth()) {
1132 while (endpar && endpar->getDepth()) {
1133 endpar = endpar->next();
1134 undoendpar = endpar;
1138 // because of parindents etc.
1139 endpar = endpar->next();
1142 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1145 Paragraph * tmppar = selection.end.par();
1146 while (tmppar != selection.start.par()->previous()) {
1147 setCursor(bview, tmppar, 0);
1148 status(bview, LyXText::NEED_MORE_REFRESH);
1149 refresh_row = cursor.row();
1150 refresh_y = cursor.y() - cursor.row()->baseline();
1151 cursor.par()->params().lineTop(line_top);
1152 cursor.par()->params().lineBottom(line_bottom);
1153 cursor.par()->params().pagebreakTop(pagebreak_top);
1154 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1155 cursor.par()->params().spaceTop(space_top);
1156 cursor.par()->params().spaceBottom(space_bottom);
1157 // does the layout allow the new alignment?
1158 if (align == LYX_ALIGN_LAYOUT)
1159 align = textclasslist
1160 .Style(bview->buffer()->params.textclass,
1161 cursor.par()->getLayout()).align;
1162 if (align & textclasslist
1163 .Style(bview->buffer()->params.textclass,
1164 cursor.par()->getLayout()).alignpossible) {
1165 if (align == textclasslist
1166 .Style(bview->buffer()->params.textclass,
1167 cursor.par()->getLayout()).align)
1168 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1170 cursor.par()->params().align(align);
1172 cursor.par()->setLabelWidthString(labelwidthstring);
1173 cursor.par()->params().noindent(noindent);
1174 tmppar = cursor.par()->previous();
1177 redoParagraphs(bview, selection.start, endpar);
1180 setCursor(bview, selection.start.par(), selection.start.pos());
1181 selection.cursor = cursor;
1182 setCursor(bview, selection.end.par(), selection.end.pos());
1183 setSelection(bview);
1184 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1186 bview->updateInset(inset_owner, true);
1190 char loweralphaCounter(int n)
1192 if (n < 1 || n > 26)
1202 char alphaCounter(int n)
1204 if (n < 1 || n > 26)
1212 char hebrewCounter(int n)
1214 static const char hebrew[22] = {
1215 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1216 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1219 if (n < 1 || n > 22)
1227 string const romanCounter(int n)
1229 static char const * roman[20] = {
1230 "i", "ii", "iii", "iv", "v",
1231 "vi", "vii", "viii", "ix", "x",
1232 "xi", "xii", "xiii", "xiv", "xv",
1233 "xvi", "xvii", "xviii", "xix", "xx"
1235 if (n < 1 || n > 20)
1244 // set the counter of a paragraph. This includes the labels
1245 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1247 LyXLayout const & layout =
1248 textclasslist.Style(buf->params.textclass,
1251 LyXTextClass const & textclass =
1252 textclasslist.TextClass(buf->params.textclass);
1254 // copy the prev-counters to this one,
1255 // unless this is the first paragraph
1256 if (par->previous()) {
1257 for (int i = 0; i < 10; ++i) {
1258 par->setCounter(i, par->previous()->getFirstCounter(i));
1260 par->params().appendix(par->previous()->params().appendix());
1261 if (!par->params().appendix() && par->params().startOfAppendix()) {
1262 par->params().appendix(true);
1263 for (int i = 0; i < 10; ++i) {
1264 par->setCounter(i, 0);
1267 par->enumdepth = par->previous()->enumdepth;
1268 par->itemdepth = par->previous()->itemdepth;
1270 for (int i = 0; i < 10; ++i) {
1271 par->setCounter(i, 0);
1273 par->params().appendix(par->params().startOfAppendix());
1278 /* Maybe we have to increment the enumeration depth.
1279 * BUT, enumeration in a footnote is considered in isolation from its
1280 * surrounding paragraph so don't increment if this is the
1281 * first line of the footnote
1282 * AND, bibliographies can't have their depth changed ie. they
1283 * are always of depth 0
1286 && par->previous()->getDepth() < par->getDepth()
1287 && textclasslist.Style(buf->params.textclass,
1288 par->previous()->getLayout()
1289 ).labeltype == LABEL_COUNTER_ENUMI
1290 && par->enumdepth < 3
1291 && layout.labeltype != LABEL_BIBLIO) {
1295 // Maybe we have to decrement the enumeration depth, see note above
1297 && par->previous()->getDepth() > par->getDepth()
1298 && layout.labeltype != LABEL_BIBLIO) {
1299 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1300 par->setCounter(6 + par->enumdepth,
1301 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1302 /* reset the counters.
1303 * A depth change is like a breaking layout
1305 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1306 par->setCounter(i, 0);
1309 if (!par->params().labelString().empty()) {
1310 par->params().labelString(string());
1313 if (layout.margintype == MARGIN_MANUAL) {
1314 if (par->params().labelWidthString().empty()) {
1315 par->setLabelWidthString(layout.labelstring());
1318 par->setLabelWidthString(string());
1321 // is it a layout that has an automatic label?
1322 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1324 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1325 if (i >= 0 && i<= buf->params.secnumdepth) {
1326 par->incCounter(i); // increment the counter
1328 // Is there a label? Useful for Chapter layout
1329 if (!par->params().appendix()) {
1330 if (!layout.labelstring().empty())
1331 par->params().labelString(layout.labelstring());
1333 par->params().labelString(string());
1335 if (!layout.labelstring_appendix().empty())
1336 par->params().labelString(layout.labelstring_appendix());
1338 par->params().labelString(string());
1343 if (!par->params().appendix()) {
1344 switch (2 * LABEL_COUNTER_CHAPTER -
1345 textclass.maxcounter() + i) {
1346 case LABEL_COUNTER_CHAPTER:
1347 s << par->getCounter(i);
1349 case LABEL_COUNTER_SECTION:
1350 s << par->getCounter(i - 1) << '.'
1351 << par->getCounter(i);
1353 case LABEL_COUNTER_SUBSECTION:
1354 s << par->getCounter(i - 2) << '.'
1355 << par->getCounter(i - 1) << '.'
1356 << par->getCounter(i);
1358 case LABEL_COUNTER_SUBSUBSECTION:
1359 s << par->getCounter(i - 3) << '.'
1360 << par->getCounter(i - 2) << '.'
1361 << par->getCounter(i - 1) << '.'
1362 << par->getCounter(i);
1365 case LABEL_COUNTER_PARAGRAPH:
1366 s << par->getCounter(i - 4) << '.'
1367 << par->getCounter(i - 3) << '.'
1368 << par->getCounter(i - 2) << '.'
1369 << par->getCounter(i - 1) << '.'
1370 << par->getCounter(i);
1372 case LABEL_COUNTER_SUBPARAGRAPH:
1373 s << par->getCounter(i - 5) << '.'
1374 << par->getCounter(i - 4) << '.'
1375 << par->getCounter(i - 3) << '.'
1376 << par->getCounter(i - 2) << '.'
1377 << par->getCounter(i - 1) << '.'
1378 << par->getCounter(i);
1382 // Can this ever be reached? And in the
1383 // case it is, how can this be correct?
1385 s << par->getCounter(i) << '.';
1388 } else { // appendix
1389 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1390 case LABEL_COUNTER_CHAPTER:
1391 if (par->isRightToLeftPar(buf->params))
1392 s << hebrewCounter(par->getCounter(i));
1394 s << alphaCounter(par->getCounter(i));
1396 case LABEL_COUNTER_SECTION:
1397 if (par->isRightToLeftPar(buf->params))
1398 s << hebrewCounter(par->getCounter(i - 1));
1400 s << alphaCounter(par->getCounter(i - 1));
1403 << par->getCounter(i);
1406 case LABEL_COUNTER_SUBSECTION:
1407 if (par->isRightToLeftPar(buf->params))
1408 s << hebrewCounter(par->getCounter(i - 2));
1410 s << alphaCounter(par->getCounter(i - 2));
1413 << par->getCounter(i-1) << '.'
1414 << par->getCounter(i);
1417 case LABEL_COUNTER_SUBSUBSECTION:
1418 if (par->isRightToLeftPar(buf->params))
1419 s << hebrewCounter(par->getCounter(i-3));
1421 s << alphaCounter(par->getCounter(i-3));
1424 << par->getCounter(i-2) << '.'
1425 << par->getCounter(i-1) << '.'
1426 << par->getCounter(i);
1429 case LABEL_COUNTER_PARAGRAPH:
1430 if (par->isRightToLeftPar(buf->params))
1431 s << hebrewCounter(par->getCounter(i-4));
1433 s << alphaCounter(par->getCounter(i-4));
1436 << par->getCounter(i-3) << '.'
1437 << par->getCounter(i-2) << '.'
1438 << par->getCounter(i-1) << '.'
1439 << par->getCounter(i);
1442 case LABEL_COUNTER_SUBPARAGRAPH:
1443 if (par->isRightToLeftPar(buf->params))
1444 s << hebrewCounter(par->getCounter(i-5));
1446 s << alphaCounter(par->getCounter(i-5));
1449 << par->getCounter(i-4) << '.'
1450 << par->getCounter(i-3) << '.'
1451 << par->getCounter(i-2) << '.'
1452 << par->getCounter(i-1) << '.'
1453 << par->getCounter(i);
1457 // Can this ever be reached? And in the
1458 // case it is, how can this be correct?
1460 s << par->getCounter(i) << '.';
1466 par->params().labelString(par->params().labelString() +s.str().c_str());
1467 // We really want to remove the c_str as soon as
1470 for (i++; i < 10; ++i) {
1471 // reset the following counters
1472 par->setCounter(i, 0);
1474 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1475 for (i++; i < 10; ++i) {
1476 // reset the following counters
1477 par->setCounter(i, 0);
1479 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1480 par->incCounter(i + par->enumdepth);
1481 int number = par->getCounter(i + par->enumdepth);
1485 switch (par->enumdepth) {
1487 if (par->isRightToLeftPar(buf->params))
1489 << hebrewCounter(number)
1493 << loweralphaCounter(number)
1497 if (par->isRightToLeftPar(buf->params))
1498 s << '.' << romanCounter(number);
1500 s << romanCounter(number) << '.';
1503 if (par->isRightToLeftPar(buf->params))
1505 << alphaCounter(number);
1507 s << alphaCounter(number)
1511 if (par->isRightToLeftPar(buf->params))
1518 par->params().labelString(s.str().c_str());
1520 for (i += par->enumdepth + 1; i < 10; ++i) {
1521 // reset the following counters
1522 par->setCounter(i, 0);
1526 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1527 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1529 int number = par->getCounter(i);
1531 InsetCommandParams p( "bibitem" );
1532 par->bibkey = new InsetBibKey(p);
1534 par->bibkey->setCounter(number);
1535 par->params().labelString(layout.labelstring());
1537 // In biblio should't be following counters but...
1539 string s = layout.labelstring();
1541 // the caption hack:
1542 if (layout.labeltype == LABEL_SENSITIVE) {
1543 bool isOK (par->inInset() && par->inInset()->owner() &&
1544 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1547 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1549 = floatList.getType(tmp->type());
1550 // We should get the correct number here too.
1551 s = fl.name() + " #:";
1553 /* par->SetLayout(0);
1554 s = layout->labelstring; */
1555 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1556 ? " :úåòîùî øñç" : "Senseless: ";
1559 par->params().labelString(s);
1561 /* reset the enumeration counter. They are always resetted
1562 * when there is any other layout between */
1563 for (int i = 6 + par->enumdepth; i < 10; ++i)
1564 par->setCounter(i, 0);
1569 // Updates all counters BEHIND the row. Changed paragraphs
1570 // with a dynamic left margin will be rebroken.
1571 void LyXText::updateCounters(BufferView * bview, Row * row) const
1579 par = row->par()->next();
1583 while (row->par() != par)
1586 setCounter(bview->buffer(), par);
1588 // now check for the headline layouts. remember that they
1589 // have a dynamic left margin
1590 if ((textclasslist.Style(bview->buffer()->params.textclass,
1591 par->layout).margintype == MARGIN_DYNAMIC
1592 || textclasslist.Style(bview->buffer()->params.textclass,
1593 par->layout).labeltype == LABEL_SENSITIVE)) {
1595 // Rebreak the paragraph
1596 removeParagraph(row);
1597 appendParagraph(bview, row);
1604 void LyXText::insertInset(BufferView * bview, Inset * inset)
1606 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1608 setUndo(bview, Undo::INSERT,
1609 cursor.par(), cursor.par()->next());
1610 cursor.par()->insertInset(cursor.pos(), inset);
1611 // Just to rebreak and refresh correctly.
1612 // The character will not be inserted a second time
1613 insertChar(bview, Paragraph::META_INSET);
1615 // If we enter a highly editable inset the cursor should be to before
1616 // the inset. This couldn't happen before as Undo was not handled inside
1617 // inset now after the Undo LyX tries to call inset->Edit(...) again
1618 // and cannot do this as the cursor is behind the inset and GetInset
1619 // does not return the inset!
1620 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1621 cursorLeft(bview, true);
1627 void LyXText::copyEnvironmentType()
1629 copylayouttype = cursor.par()->getLayout();
1633 void LyXText::pasteEnvironmentType(BufferView * bview)
1635 setLayout(bview, copylayouttype);
1639 void LyXText::cutSelection(BufferView * bview, bool doclear)
1641 // Stuff what we got on the clipboard. Even if there is no selection.
1643 // There is a problem with having the stuffing here in that the
1644 // larger the selection the slower LyX will get. This can be
1645 // solved by running the line below only when the selection has
1646 // finished. The solution used currently just works, to make it
1647 // faster we need to be more clever and probably also have more
1648 // calls to stuffClipboard. (Lgb)
1649 bview->stuffClipboard(selectionAsString(bview->buffer()));
1651 // This doesn't make sense, if there is no selection
1652 if (!selection.set())
1655 // OK, we have a selection. This is always between selection.start
1656 // and selection.end
1658 // make sure that the depth behind the selection are restored, too
1659 Paragraph * endpar = selection.end.par()->next();
1660 Paragraph * undoendpar = endpar;
1662 if (endpar && endpar->getDepth()) {
1663 while (endpar && endpar->getDepth()) {
1664 endpar = endpar->next();
1665 undoendpar = endpar;
1667 } else if (endpar) {
1668 endpar = endpar->next(); // because of parindents etc.
1671 setUndo(bview, Undo::DELETE,
1672 selection.start.par(), undoendpar);
1674 // there are two cases: cut only within one paragraph or
1675 // more than one paragraph
1676 if (selection.start.par() == selection.end.par()) {
1677 // only within one paragraph
1678 endpar = selection.end.par();
1679 int pos = selection.end.pos();
1680 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1681 selection.start.pos(), pos,
1682 bview->buffer()->params.textclass, doclear);
1683 selection.end.pos(pos);
1685 endpar = selection.end.par();
1686 int pos = selection.end.pos();
1687 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1688 selection.start.pos(), pos,
1689 bview->buffer()->params.textclass, doclear);
1691 selection.end.par(endpar);
1692 selection.end.pos(pos);
1693 cursor.pos(selection.end.pos());
1695 endpar = endpar->next();
1697 // sometimes necessary
1699 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1701 redoParagraphs(bview, selection.start, endpar);
1703 // cutSelection can invalidate the cursor so we need to set
1705 cursor = selection.start;
1707 // need a valid cursor. (Lgb)
1710 setCursor(bview, cursor.par(), cursor.pos());
1711 selection.cursor = cursor;
1712 updateCounters(bview, cursor.row());
1716 void LyXText::copySelection(BufferView * bview)
1718 // Stuff what we got on the clipboard. Even if there is no selection.
1720 // There is a problem with having the stuffing here in that the
1721 // larger the selection the slower LyX will get. This can be
1722 // solved by running the line below only when the selection has
1723 // finished. The solution used currently just works, to make it
1724 // faster we need to be more clever and probably also have more
1725 // calls to stuffClipboard. (Lgb)
1726 bview->stuffClipboard(selectionAsString(bview->buffer()));
1728 // this doesnt make sense, if there is no selection
1729 if (!selection.set())
1732 // ok we have a selection. This is always between selection.start
1733 // and sel_end cursor
1735 // copy behind a space if there is one
1736 while (selection.start.par()->size() > selection.start.pos()
1737 && selection.start.par()->isLineSeparator(selection.start.pos())
1738 && (selection.start.par() != selection.end.par()
1739 || selection.start.pos() < selection.end.pos()))
1740 selection.start.pos(selection.start.pos() + 1);
1742 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1743 selection.start.pos(), selection.end.pos(),
1744 bview->buffer()->params.textclass);
1748 void LyXText::pasteSelection(BufferView * bview)
1750 // this does not make sense, if there is nothing to paste
1751 if (!CutAndPaste::checkPastePossible(cursor.par()))
1754 setUndo(bview, Undo::INSERT,
1755 cursor.par(), cursor.par()->next());
1758 Paragraph * actpar = cursor.par();
1760 int pos = cursor.pos();
1761 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1762 bview->buffer()->params.textclass);
1764 redoParagraphs(bview, cursor, endpar);
1766 setCursor(bview, cursor.par(), cursor.pos());
1769 selection.cursor = cursor;
1770 setCursor(bview, actpar, pos);
1771 setSelection(bview);
1772 updateCounters(bview, cursor.row());
1776 // returns a pointer to the very first Paragraph
1777 Paragraph * LyXText::firstParagraph() const
1779 return ownerParagraph();
1783 // sets the selection over the number of characters of string, no check!!
1784 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1789 selection.cursor = cursor;
1790 for (string::size_type i = 0; i < str.length(); ++i)
1792 setSelection(bview);
1796 // simple replacing. The font of the first selected character is used
1797 void LyXText::replaceSelectionWithString(BufferView * bview,
1800 setCursorParUndo(bview);
1803 if (!selection.set()) { // create a dummy selection
1804 selection.end = cursor;
1805 selection.start = cursor;
1808 // Get font setting before we cut
1809 Paragraph::size_type pos = selection.end.pos();
1810 LyXFont const font = selection.start.par()
1811 ->getFontSettings(bview->buffer()->params,
1812 selection.start.pos());
1814 // Insert the new string
1815 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1816 selection.end.par()->insertChar(pos, (*cit), font);
1820 // Cut the selection
1821 cutSelection(bview);
1827 // needed to insert the selection
1828 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1830 Paragraph * par = cursor.par();
1831 Paragraph::size_type pos = cursor.pos();
1832 Paragraph * endpar = cursor.par()->next();
1834 setCursorParUndo(bview);
1836 // only to be sure, should not be neccessary
1839 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1841 redoParagraphs(bview, cursor, endpar);
1842 setCursor(bview, cursor.par(), cursor.pos());
1843 selection.cursor = cursor;
1844 setCursor(bview, par, pos);
1845 setSelection(bview);
1849 // turns double-CR to single CR, others where converted into one
1850 // blank. Then InsertStringAsLines is called
1851 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1853 string linestr(str);
1854 bool newline_inserted = false;
1855 for (string::size_type i = 0; i < linestr.length(); ++i) {
1856 if (linestr[i] == '\n') {
1857 if (newline_inserted) {
1858 // we know that \r will be ignored by
1859 // InsertStringA. Of course, it is a dirty
1860 // trick, but it works...
1861 linestr[i - 1] = '\r';
1865 newline_inserted = true;
1867 } else if (IsPrintable(linestr[i])) {
1868 newline_inserted = false;
1871 insertStringAsLines(bview, linestr);
1875 bool LyXText::gotoNextInset(BufferView * bview,
1876 std::vector<Inset::Code> const & codes,
1877 string const & contents) const
1879 LyXCursor res = cursor;
1882 if (res.pos() < res.par()->size() - 1) {
1883 res.pos(res.pos() + 1);
1885 res.par(res.par()->next());
1889 } while (res.par() &&
1890 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1891 && (inset = res.par()->getInset(res.pos())) != 0
1892 && find(codes.begin(), codes.end(), inset->lyxCode())
1894 && (contents.empty() ||
1895 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1899 setCursor(bview, res.par(), res.pos());
1906 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1907 Paragraph::size_type pos)
1909 LyXCursor tmpcursor;
1912 Paragraph::size_type z;
1913 Row * row = getRow(par, pos, y);
1915 // is there a break one row above
1916 if (row->previous() && row->previous()->par() == row->par()) {
1917 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1918 if (z >= row->pos()) {
1919 // set the dimensions of the row above
1920 y -= row->previous()->height();
1922 refresh_row = row->previous();
1923 status(bview, LyXText::NEED_MORE_REFRESH);
1925 breakAgain(bview, row->previous());
1927 // set the cursor again. Otherwise
1928 // dangling pointers are possible
1929 setCursor(bview, cursor.par(), cursor.pos(),
1930 false, cursor.boundary());
1931 selection.cursor = cursor;
1936 int const tmpheight = row->height();
1937 Paragraph::size_type const tmplast = rowLast(row);
1941 breakAgain(bview, row);
1942 if (row->height() == tmpheight && rowLast(row) == tmplast)
1943 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1945 status(bview, LyXText::NEED_MORE_REFRESH);
1947 // check the special right address boxes
1948 if (textclasslist.Style(bview->buffer()->params.textclass,
1949 par->getLayout()).margintype
1950 == MARGIN_RIGHT_ADDRESS_BOX) {
1957 redoDrawingOfParagraph(bview, tmpcursor);
1960 // set the cursor again. Otherwise dangling pointers are possible
1961 // also set the selection
1963 if (selection.set()) {
1965 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1966 false, selection.cursor.boundary());
1967 selection.cursor = cursor;
1968 setCursorIntern(bview, selection.start.par(),
1969 selection.start.pos(),
1970 false, selection.start.boundary());
1971 selection.start = cursor;
1972 setCursorIntern(bview, selection.end.par(),
1973 selection.end.pos(),
1974 false, selection.end.boundary());
1975 selection.end = cursor;
1976 setCursorIntern(bview, last_sel_cursor.par(),
1977 last_sel_cursor.pos(),
1978 false, last_sel_cursor.boundary());
1979 last_sel_cursor = cursor;
1982 setCursorIntern(bview, cursor.par(), cursor.pos(),
1983 false, cursor.boundary());
1987 // returns false if inset wasn't found
1988 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1990 // first check the current paragraph
1991 int pos = cursor.par()->getPositionOfInset(inset);
1993 checkParagraph(bview, cursor.par(), pos);
1997 // check every paragraph
1999 Paragraph * par = firstParagraph();
2001 pos = par->getPositionOfInset(inset);
2003 checkParagraph(bview, par, pos);
2013 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2014 Paragraph::size_type pos,
2015 bool setfont, bool boundary) const
2017 LyXCursor old_cursor = cursor;
2018 setCursorIntern(bview, par, pos, setfont, boundary);
2019 deleteEmptyParagraphMechanism(bview, old_cursor);
2023 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2024 Paragraph::size_type pos, bool boundary) const
2028 cur.boundary(boundary);
2030 // get the cursor y position in text
2032 Row * row = getRow(par, pos, y);
2033 // y is now the beginning of the cursor row
2034 y += row->baseline();
2035 // y is now the cursor baseline
2038 // now get the cursors x position
2040 float fill_separator;
2042 float fill_label_hfill;
2043 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2045 Paragraph::size_type cursor_vpos = 0;
2046 Paragraph::size_type last = rowLastPrintable(row);
2048 if (pos > last + 1) // This shouldn't happen.
2050 else if (pos < row->pos())
2053 if (last < row->pos())
2054 cursor_vpos = row->pos();
2055 else if (pos > last && !boundary)
2056 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2057 ? row->pos() : last + 1;
2058 else if (pos > row->pos() &&
2059 (pos > last || boundary))
2060 /// Place cursor after char at (logical) position pos - 1
2061 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2062 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2064 /// Place cursor before char at (logical) position pos
2065 cursor_vpos = (bidi_level(pos) % 2 == 0)
2066 ? log2vis(pos) : log2vis(pos) + 1;
2068 Paragraph::size_type main_body =
2069 beginningOfMainBody(bview->buffer(), row->par());
2070 if ((main_body > 0) &&
2071 ((main_body-1 > last) ||
2072 !row->par()->isLineSeparator(main_body-1)))
2075 for (Paragraph::size_type vpos = row->pos();
2076 vpos < cursor_vpos; ++vpos) {
2077 pos = vis2log(vpos);
2078 if (main_body > 0 && pos == main_body - 1) {
2079 x += fill_label_hfill +
2080 lyxfont::width(textclasslist.Style(
2081 bview->buffer()->params.textclass,
2082 row->par()->getLayout())
2084 getFont(bview->buffer(), row->par(), -2));
2085 if (row->par()->isLineSeparator(main_body-1))
2086 x -= singleWidth(bview, row->par(),main_body-1);
2088 if (hfillExpansion(bview->buffer(), row, pos)) {
2089 x += singleWidth(bview, row->par(), pos);
2090 if (pos >= main_body)
2093 x += fill_label_hfill;
2094 } else if (row->par()->isSeparator(pos)) {
2095 x += singleWidth(bview, row->par(), pos);
2096 if (pos >= main_body)
2097 x += fill_separator;
2099 x += singleWidth(bview, row->par(), pos);
2108 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2109 Paragraph::size_type pos,
2110 bool setfont, bool boundary) const
2112 InsetText * it = static_cast<InsetText *>(par->inInset());
2113 if (it && (it != inset_owner)) {
2114 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2117 setCursor(bview, cursor, par, pos, boundary);
2119 setCurrentFont(bview);
2124 void LyXText::setCurrentFont(BufferView * bview) const
2126 Paragraph::size_type pos = cursor.pos();
2127 if (cursor.boundary() && pos > 0)
2131 if (pos == cursor.par()->size())
2133 else // potentional bug... BUG (Lgb)
2134 if (cursor.par()->isSeparator(pos)) {
2135 if (pos > cursor.row()->pos() &&
2136 bidi_level(pos) % 2 ==
2137 bidi_level(pos - 1) % 2)
2139 else if (pos + 1 < cursor.par()->size())
2145 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2146 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2148 if (cursor.pos() == cursor.par()->size() &&
2149 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2150 !cursor.boundary()) {
2151 Language const * lang =
2152 cursor.par()->getParLanguage(bview->buffer()->params);
2153 current_font.setLanguage(lang);
2154 current_font.setNumber(LyXFont::OFF);
2155 real_current_font.setLanguage(lang);
2156 real_current_font.setNumber(LyXFont::OFF);
2161 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2163 LyXCursor old_cursor = cursor;
2166 // Get the row first.
2168 Row * row = getRowNearY(y);
2171 int column = getColumnNearX(bview, row, x, bound);
2173 cursor.par(row->par());
2174 cursor.pos(row->pos() + column);
2176 cursor.y(y + row->baseline());
2178 cursor.boundary(bound);
2180 setCursorFromCoordinates(bview, cursor, x, y);
2182 setCurrentFont(bview);
2183 deleteEmptyParagraphMechanism(bview, old_cursor);
2187 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2190 // Get the row first.
2192 Row * row = getRowNearY(y);
2194 int const column = getColumnNearX(bview, row, x, bound);
2196 cur.par(row->par());
2197 cur.pos(row->pos() + column);
2199 cur.y(y + row->baseline());
2201 cur.boundary(bound);
2205 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2207 if (cursor.pos() > 0) {
2208 bool boundary = cursor.boundary();
2209 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2210 if (!internal && !boundary &&
2211 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2212 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2213 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2214 Paragraph * par = cursor.par()->previous();
2215 setCursor(bview, par, par->size());
2220 void LyXText::cursorRight(BufferView * bview, bool internal) const
2222 if (!internal && cursor.boundary() &&
2223 !cursor.par()->isNewline(cursor.pos()))
2224 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2225 else if (cursor.pos() < cursor.par()->size()) {
2226 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2228 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2229 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2230 } else if (cursor.par()->next())
2231 setCursor(bview, cursor.par()->next(), 0);
2235 void LyXText::cursorUp(BufferView * bview) const
2237 setCursorFromCoordinates(bview, cursor.x_fix(),
2238 cursor.y() - cursor.row()->baseline() - 1);
2242 void LyXText::cursorDown(BufferView * bview) const
2244 setCursorFromCoordinates(bview, cursor.x_fix(),
2245 cursor.y() - cursor.row()->baseline()
2246 + cursor.row()->height() + 1);
2250 void LyXText::cursorUpParagraph(BufferView * bview) const
2252 if (cursor.pos() > 0) {
2253 setCursor(bview, cursor.par(), 0);
2255 else if (cursor.par()->previous()) {
2256 setCursor(bview, cursor.par()->previous(), 0);
2261 void LyXText::cursorDownParagraph(BufferView * bview) const
2263 if (cursor.par()->next()) {
2264 setCursor(bview, cursor.par()->next(), 0);
2266 setCursor(bview, cursor.par(), cursor.par()->size());
2271 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2272 LyXCursor const & old_cursor) const
2274 // Would be wrong to delete anything if we have a selection.
2275 if (selection.set()) return;
2277 // We allow all kinds of "mumbo-jumbo" when freespacing.
2278 if (textclasslist.Style(bview->buffer()->params.textclass,
2279 old_cursor.par()->getLayout()).free_spacing)
2282 bool deleted = false;
2284 /* Ok I'll put some comments here about what is missing.
2285 I have fixed BackSpace (and thus Delete) to not delete
2286 double-spaces automagically. I have also changed Cut,
2287 Copy and Paste to hopefully do some sensible things.
2288 There are still some small problems that can lead to
2289 double spaces stored in the document file or space at
2290 the beginning of paragraphs. This happens if you have
2291 the cursor betwenn to spaces and then save. Or if you
2292 cut and paste and the selection have a space at the
2293 beginning and then save right after the paste. I am
2294 sure none of these are very hard to fix, but I will
2295 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2296 that I can get some feedback. (Lgb)
2299 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2300 // delete the LineSeparator.
2303 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2304 // delete the LineSeparator.
2307 // If the pos around the old_cursor were spaces, delete one of them.
2308 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2309 // Only if the cursor has really moved
2311 if (old_cursor.pos() > 0
2312 && old_cursor.pos() < old_cursor.par()->size()
2313 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2314 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2315 old_cursor.par()->erase(old_cursor.pos() - 1);
2316 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2318 if (old_cursor.par() == cursor.par() &&
2319 cursor.pos() > old_cursor.pos()) {
2320 setCursorIntern(bview, cursor.par(),
2323 setCursorIntern(bview, cursor.par(),
2329 // Do not delete empty paragraphs with keepempty set.
2330 if ((textclasslist.Style(bview->buffer()->params.textclass,
2331 old_cursor.par()->getLayout())).keepempty)
2334 LyXCursor tmpcursor;
2336 if (old_cursor.par() != cursor.par()) {
2337 if ((old_cursor.par()->size() == 0
2338 || (old_cursor.par()->size() == 1
2339 && old_cursor.par()->isLineSeparator(0)))) {
2340 // ok, we will delete anything
2342 // make sure that you do not delete any environments
2343 status(bview, LyXText::NEED_MORE_REFRESH);
2346 if (old_cursor.row()->previous()) {
2347 refresh_row = old_cursor.row()->previous();
2348 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2350 cursor = old_cursor; // that undo can restore the right cursor position
2351 Paragraph * endpar = old_cursor.par()->next();
2352 if (endpar && endpar->getDepth()) {
2353 while (endpar && endpar->getDepth()) {
2354 endpar = endpar->next();
2357 setUndo(bview, Undo::DELETE,
2363 removeRow(old_cursor.row());
2364 if (ownerParagraph() == old_cursor.par()) {
2365 ownerParagraph(ownerParagraph()->next());
2368 delete old_cursor.par();
2370 /* Breakagain the next par. Needed
2371 * because of the parindent that
2372 * can occur or dissappear. The
2373 * next row can change its height,
2374 * if there is another layout before */
2375 if (refresh_row->next()) {
2376 breakAgain(bview, refresh_row->next());
2377 updateCounters(bview, refresh_row);
2379 setHeightOfRow(bview, refresh_row);
2381 refresh_row = old_cursor.row()->next();
2382 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2385 cursor = old_cursor; // that undo can restore the right cursor position
2386 Paragraph * endpar = old_cursor.par()->next();
2387 if (endpar && endpar->getDepth()) {
2388 while (endpar && endpar->getDepth()) {
2389 endpar = endpar->next();
2392 setUndo(bview, Undo::DELETE,
2398 removeRow(old_cursor.row());
2400 if (ownerParagraph() == old_cursor.par()) {
2401 ownerParagraph(ownerParagraph()->next());
2404 delete old_cursor.par();
2406 /* Breakagain the next par. Needed
2407 because of the parindent that can
2408 occur or dissappear.
2409 The next row can change its height,
2410 if there is another layout before
2413 breakAgain(bview, refresh_row);
2414 updateCounters(bview, refresh_row->previous());
2420 setCursorIntern(bview, cursor.par(), cursor.pos());
2422 if (selection.cursor.par() == old_cursor.par()
2423 && selection.cursor.pos() == selection.cursor.pos()) {
2424 // correct selection
2425 selection.cursor = cursor;
2429 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2430 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2432 setCursorIntern(bview, cursor.par(), cursor.pos());
2433 selection.cursor = cursor;
2440 void LyXText::toggleAppendix(BufferView * bview)
2442 Paragraph * par = cursor.par();
2443 bool start = !par->params().startOfAppendix();
2445 // ensure that we have only one start_of_appendix in this document
2446 Paragraph * tmp = firstParagraph();
2447 for (; tmp; tmp = tmp->next()) {
2448 tmp->params().startOfAppendix(false);
2451 par->params().startOfAppendix(start);
2453 // we can set the refreshing parameters now
2454 status(bview, LyXText::NEED_MORE_REFRESH);
2456 refresh_row = 0; // not needed for full update
2457 updateCounters(bview, 0);
2458 setCursor(bview, cursor.par(), cursor.pos());
2462 Paragraph * LyXText::ownerParagraph() const
2465 return inset_owner->paragraph();
2467 return bv_owner->buffer()->paragraph;
2471 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2474 inset_owner->paragraph(p);
2476 bv_owner->buffer()->paragraph = p;
2481 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2483 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2484 if (op && op->inInset()) {
2485 static_cast<InsetText *>(op->inInset())->paragraph(p);
2488 inset_owner->paragraph(p);
2490 bv_owner->buffer()->paragraph = p;
2497 LyXText::text_status LyXText::status() const
2503 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2506 if ((status_ != NEED_MORE_REFRESH)
2507 || (status_ == NEED_MORE_REFRESH)
2508 && (st != NEED_VERY_LITTLE_REFRESH)) {
2510 if (inset_owner && st != UNCHANGED) {
2511 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2515 #warning Please tell what the intention is here. (Lgb)
2516 // The above does not make any sense, I changed it to what is here,
2517 // but it still does not make much sense. (Lgb)
2518 #warning Sure have a look now! (Jug)
2519 // well as much as I know && binds more then || so the above and the
2520 // below are identical (this for your known use of parentesis!)
2521 // Now some explanation:
2522 // We should only go up with refreshing code so this means that if
2523 // we have a MORE refresh we should never set it to LITTLE if we still
2524 // didn't handle it (and then it will be UNCHANGED. Now as long as
2525 // we stay inside one LyXText this may work but we need to tell the
2526 // outermost LyXText that it should REALLY draw us if there is some
2527 // change in a Inset::LyXText. So you see that when we are inside a
2528 // inset's LyXText we give the LITTLE to the outermost LyXText to
2529 // tell'em that it should redraw the actual row (where the inset
2530 // resides! Capito?!
2532 if ((status_ != NEED_MORE_REFRESH)
2533 || (status_ == NEED_MORE_REFRESH
2534 && st != NEED_VERY_LITTLE_REFRESH))
2537 if (inset_owner && st != UNCHANGED) {
2538 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2539 if (!bview->text->refresh_row) {
2540 bview->text->refresh_row = bview->text->cursor.row();
2541 bview->text->refresh_y = bview->text->cursor.y() -
2542 bview->text->cursor.row()->baseline();