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 "undo_funcs.h"
30 #include "bufferparams.h"
31 #include "lyx_gui_misc.h"
33 #include "BufferView.h"
35 #include "CutAndPaste.h"
40 #include "FloatList.h"
42 #include "ParagraphParameters.h"
51 LyXText::LyXText(BufferView * bv)
52 : number_of_rows(0), height(0), width(0), first(0),
53 bv_owner(bv), inset_owner(0), the_locking_inset(0),
54 need_break_row(0), refresh_y(0), refresh_row(0),
55 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
60 LyXText::LyXText(InsetText * inset)
61 : number_of_rows(0), height(0), width(0), first(0),
62 bv_owner(0), inset_owner(inset), the_locking_inset(0),
63 need_break_row(0), refresh_y(0), refresh_row(0),
64 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
68 void LyXText::init(BufferView * bview, bool reinit)
71 // Delete all rows, this does not touch the paragraphs!
72 Row * tmprow = firstrow;
74 tmprow = firstrow->next();
78 lastrow = refresh_row = need_break_row = 0;
79 width = height = copylayouttype = 0;
80 number_of_rows = first = refresh_y = 0;
81 status_ = LyXText::UNCHANGED;
85 Paragraph * par = ownerParagraph();
86 current_font = getFont(bview->buffer(), par, 0);
88 insertParagraph(bview, par, lastrow);
91 setCursorIntern(bview, firstrow->par(), 0);
92 selection.cursor = cursor;
98 // Delete all rows, this does not touch the paragraphs!
99 Row * tmprow = firstrow;
101 tmprow = firstrow->next();
108 // Gets the fully instantiated font at a given position in a paragraph
109 // Basically the same routine as Paragraph::getFont() in paragraph.C.
110 // The difference is that this one is used for displaying, and thus we
111 // are allowed to make cosmetic improvements. For instance make footnotes
113 // If position is -1, we get the layout font of the paragraph.
114 // If position is -2, we get the font of the manual label of the paragraph.
115 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
116 Paragraph::size_type pos) const
118 LyXLayout const & layout =
119 textclasslist.Style(buf->params.textclass, par->getLayout());
121 Paragraph::depth_type par_depth = par->getDepth();
122 // We specialize the 95% common case:
126 if (layout.labeltype == LABEL_MANUAL
127 && pos < beginningOfMainBody(buf, par)) {
129 LyXFont f = par->getFontSettings(buf->params,
131 return f.realize(layout.reslabelfont, buf->params.language);
133 LyXFont f = par->getFontSettings(buf->params, pos);
134 return f.realize(layout.resfont, buf->params.language);
139 // process layoutfont for pos == -1 and labelfont for pos < -1
141 return layout.resfont;
143 return layout.reslabelfont;
147 // The uncommon case need not be optimized as much
149 LyXFont layoutfont, tmpfont;
153 if (pos < beginningOfMainBody(buf, par)) {
155 layoutfont = layout.labelfont;
158 layoutfont = layout.font;
160 tmpfont = par->getFontSettings(buf->params, pos);
161 tmpfont.realize(layoutfont, buf->params.language);
164 // process layoutfont for pos == -1 and labelfont for pos < -1
166 tmpfont = layout.font;
168 tmpfont = layout.labelfont;
171 // Resolve against environment font information
172 while (par && par_depth && !tmpfont.resolved()) {
173 par = par->outerHook();
175 tmpfont.realize(textclasslist.
176 Style(buf->params.textclass,
177 par->getLayout()).font,
178 buf->params.language);
179 par_depth = par->getDepth();
183 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
184 buf->params.language);
190 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
191 Paragraph::size_type pos, LyXFont const & fnt,
194 Buffer const * buf = bv->buffer();
195 LyXFont font = getFont(buf, par, pos);
196 font.update(fnt, toggleall);
197 // Let the insets convert their font
198 if (par->getChar(pos) == Paragraph::META_INSET) {
199 Inset * inset = par->getInset(pos);
201 if (inset->editable()==Inset::HIGHLY_EDITABLE) {
202 UpdatableInset * uinset =
203 static_cast<UpdatableInset *>(inset);
204 uinset->setFont(bv, fnt, toggleall, true);
206 font = inset->convertFont(font);
210 LyXLayout const & layout =
211 textclasslist.Style(buf->params.textclass,
214 // Get concrete layout font to reduce against
217 if (pos < beginningOfMainBody(buf, par))
218 layoutfont = layout.labelfont;
220 layoutfont = layout.font;
222 // Realize against environment font information
223 if (par->getDepth()){
224 Paragraph * tp = par;
225 while (!layoutfont.resolved() && tp && tp->getDepth()) {
226 tp = tp->outerHook();
228 layoutfont.realize(textclasslist.
229 Style(buf->params.textclass,
230 tp->getLayout()).font,
231 buf->params.language);
235 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
236 buf->params.language);
238 // Now, reduce font against full layout font
239 font.reduce(layoutfont);
241 par->setFont(pos, font);
245 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
246 Paragraph::size_type pos, LyXFont const & fnt)
249 // Let the insets convert their font
250 if (par->getChar(pos) == Paragraph::META_INSET) {
251 font = par->getInset(pos)->convertFont(font);
254 LyXLayout const & layout =
255 textclasslist.Style(buf->params.textclass,
258 // Get concrete layout font to reduce against
261 if (pos < beginningOfMainBody(buf, par))
262 layoutfont = layout.labelfont;
264 layoutfont = layout.font;
266 // Realize against environment font information
267 if (par->getDepth()){
268 Paragraph * tp = par;
269 while (!layoutfont.resolved() && tp && tp->getDepth()) {
270 tp = tp->outerHook();
272 layoutfont.realize(textclasslist.
273 Style(buf->params.textclass,
274 tp->getLayout()).font,
275 buf->params.language);
279 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
280 buf->params.language);
282 // Now, reduce font against full layout font
283 font.reduce(layoutfont);
285 par->setFont(pos, font);
289 // inserts a new row behind the specified row, increments
290 // the touched counters
291 void LyXText::insertRow(Row * row, Paragraph * par,
292 Paragraph::size_type pos) const
294 Row * tmprow = new Row;
297 tmprow->next(firstrow);
300 tmprow->previous(row);
301 tmprow->next(row->next());
306 tmprow->next()->previous(tmprow);
308 if (tmprow->previous())
309 tmprow->previous()->next(tmprow);
321 // removes the row and reset the touched counters
322 void LyXText::removeRow(Row * row) const
324 /* this must not happen before the currentrow for clear reasons.
325 so the trick is just to set the current row onto the previous
328 getRow(row->par(), row->pos(), unused_y);
331 row->next()->previous(row->previous());
332 if (!row->previous()) {
333 firstrow = row->next();
335 row->previous()->next(row->next());
338 lastrow = row->previous();
340 height -= row->height(); // the text becomes smaller
343 --number_of_rows; // one row less
347 // remove all following rows of the paragraph of the specified row.
348 void LyXText::removeParagraph(Row * row) const
350 Paragraph * tmppar = row->par();
354 while (row && row->par() == tmppar) {
355 tmprow = row->next();
362 // insert the specified paragraph behind the specified row
363 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
366 insertRow(row, par, 0); /* insert a new row, starting
369 setCounter(bview->buffer(), par); // set the counters
371 // and now append the whole paragraph behind the new row
374 appendParagraph(bview, firstrow);
376 row->next()->height(0);
377 appendParagraph(bview, row->next());
382 Inset * LyXText::getInset() const
385 if (cursor.pos() == 0 && cursor.par()->bibkey) {
386 inset = cursor.par()->bibkey;
387 } else if (cursor.pos() < cursor.par()->size()
388 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
389 inset = cursor.par()->getInset(cursor.pos());
395 void LyXText::toggleInset(BufferView * bview)
397 Inset * inset = getInset();
398 if (!inset->editable())
400 //bview->owner()->message(inset->editMessage());
402 // do we want to keep this?? (JMarc)
403 if (inset->editable() != Inset::HIGHLY_EDITABLE)
404 setCursorParUndo(bview);
406 if (inset->isOpen()) {
412 inset->open(bview, !inset->isOpen());
417 /* used in setlayout */
418 // Asger is not sure we want to do this...
419 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
422 LyXLayout const & layout =
423 textclasslist.Style(buf->params.textclass, par->getLayout());
425 LyXFont layoutfont, tmpfont;
426 for (Paragraph::size_type pos = 0;
427 pos < par->size(); ++pos) {
428 if (pos < beginningOfMainBody(buf, par))
429 layoutfont = layout.labelfont;
431 layoutfont = layout.font;
433 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());
526 clearSelection(bview);
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());
607 clearSelection(bview);
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());
664 clearSelection(bview);
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());
726 clearSelection(bview);
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(BufferView * /*bview*/) 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) {
1051 clearSelection(bview);
1052 cursor = resetCursor;
1053 setCursor(bview, cursor.par(), cursor.pos());
1054 selection.cursor = cursor;
1057 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1061 Paragraph::size_type
1062 LyXText::beginningOfMainBody(Buffer const * buf,
1063 Paragraph const * par) const
1065 if (textclasslist.Style(buf->params.textclass,
1066 par->getLayout()).labeltype != LABEL_MANUAL)
1069 return par->beginningOfMainBody();
1073 /* the DTP switches for paragraphs. LyX will store them in the
1074 * first physicla paragraph. When a paragraph is broken, the top settings
1075 * rest, the bottom settings are given to the new one. So I can make shure,
1076 * they do not duplicate themself and you cannnot make dirty things with
1079 void LyXText::setParagraph(BufferView * bview,
1080 bool line_top, bool line_bottom,
1081 bool pagebreak_top, bool pagebreak_bottom,
1082 VSpace const & space_top,
1083 VSpace const & space_bottom,
1085 string labelwidthstring,
1088 LyXCursor tmpcursor = cursor;
1089 if (!selection.set()) {
1090 selection.start = cursor;
1091 selection.end = cursor;
1094 // make sure that the depth behind the selection are restored, too
1095 Paragraph * endpar = selection.end.par()->next();
1096 Paragraph * undoendpar = endpar;
1098 if (endpar && endpar->getDepth()) {
1099 while (endpar && endpar->getDepth()) {
1100 endpar = endpar->next();
1101 undoendpar = endpar;
1105 // because of parindents etc.
1106 endpar = endpar->next();
1109 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1112 Paragraph * tmppar = selection.end.par();
1113 while (tmppar != selection.start.par()->previous()) {
1114 setCursor(bview, tmppar, 0);
1115 status(bview, LyXText::NEED_MORE_REFRESH);
1116 refresh_row = cursor.row();
1117 refresh_y = cursor.y() - cursor.row()->baseline();
1118 cursor.par()->params().lineTop(line_top);
1119 cursor.par()->params().lineBottom(line_bottom);
1120 cursor.par()->params().pagebreakTop(pagebreak_top);
1121 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1122 cursor.par()->params().spaceTop(space_top);
1123 cursor.par()->params().spaceBottom(space_bottom);
1124 // does the layout allow the new alignment?
1125 if (align == LYX_ALIGN_LAYOUT)
1126 align = textclasslist
1127 .Style(bview->buffer()->params.textclass,
1128 cursor.par()->getLayout()).align;
1129 if (align & textclasslist
1130 .Style(bview->buffer()->params.textclass,
1131 cursor.par()->getLayout()).alignpossible) {
1132 if (align == textclasslist
1133 .Style(bview->buffer()->params.textclass,
1134 cursor.par()->getLayout()).align)
1135 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1137 cursor.par()->params().align(align);
1139 cursor.par()->setLabelWidthString(labelwidthstring);
1140 cursor.par()->params().noindent(noindent);
1141 tmppar = cursor.par()->previous();
1144 redoParagraphs(bview, selection.start, endpar);
1146 clearSelection(bview);
1147 setCursor(bview, selection.start.par(), selection.start.pos());
1148 selection.cursor = cursor;
1149 setCursor(bview, selection.end.par(), selection.end.pos());
1150 setSelection(bview);
1151 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1153 bview->updateInset(inset_owner, true);
1157 char loweralphaCounter(int n)
1159 if (n < 1 || n > 26)
1169 char alphaCounter(int n)
1171 if (n < 1 || n > 26)
1179 char hebrewCounter(int n)
1181 static const char hebrew[22] = {
1182 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1183 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1186 if (n < 1 || n > 22)
1194 string const romanCounter(int n)
1196 static char const * roman[20] = {
1197 "i", "ii", "iii", "iv", "v",
1198 "vi", "vii", "viii", "ix", "x",
1199 "xi", "xii", "xiii", "xiv", "xv",
1200 "xvi", "xvii", "xviii", "xix", "xx"
1202 if (n < 1 || n > 20)
1211 // set the counter of a paragraph. This includes the labels
1212 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1214 LyXLayout const & layout =
1215 textclasslist.Style(buf->params.textclass,
1218 LyXTextClass const & textclass =
1219 textclasslist.TextClass(buf->params.textclass);
1221 // copy the prev-counters to this one,
1222 // unless this is the first paragraph
1223 if (par->previous()) {
1224 for (int i = 0; i < 10; ++i) {
1225 par->setCounter(i, par->previous()->getFirstCounter(i));
1227 par->params().appendix(par->previous()->params().appendix());
1228 if (!par->params().appendix() && par->params().startOfAppendix()) {
1229 par->params().appendix(true);
1230 for (int i = 0; i < 10; ++i) {
1231 par->setCounter(i, 0);
1234 par->enumdepth = par->previous()->enumdepth;
1235 par->itemdepth = par->previous()->itemdepth;
1237 for (int i = 0; i < 10; ++i) {
1238 par->setCounter(i, 0);
1240 par->params().appendix(par->params().startOfAppendix());
1245 /* Maybe we have to increment the enumeration depth.
1246 * BUT, enumeration in a footnote is considered in isolation from its
1247 * surrounding paragraph so don't increment if this is the
1248 * first line of the footnote
1249 * AND, bibliographies can't have their depth changed ie. they
1250 * are always of depth 0
1253 && par->previous()->getDepth() < par->getDepth()
1254 && textclasslist.Style(buf->params.textclass,
1255 par->previous()->getLayout()
1256 ).labeltype == LABEL_COUNTER_ENUMI
1257 && par->enumdepth < 3
1258 && layout.labeltype != LABEL_BIBLIO) {
1262 // Maybe we have to decrement the enumeration depth, see note above
1264 && par->previous()->getDepth() > par->getDepth()
1265 && layout.labeltype != LABEL_BIBLIO) {
1266 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1267 par->setCounter(6 + par->enumdepth,
1268 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1269 /* reset the counters.
1270 * A depth change is like a breaking layout
1272 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1273 par->setCounter(i, 0);
1276 if (!par->params().labelString().empty()) {
1277 par->params().labelString(string());
1280 if (layout.margintype == MARGIN_MANUAL) {
1281 if (par->params().labelWidthString().empty()) {
1282 par->setLabelWidthString(layout.labelstring());
1285 par->setLabelWidthString(string());
1288 // is it a layout that has an automatic label?
1289 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1291 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1292 if (i >= 0 && i<= buf->params.secnumdepth) {
1293 par->incCounter(i); // increment the counter
1295 // Is there a label? Useful for Chapter layout
1296 if (!par->params().appendix()) {
1297 if (!layout.labelstring().empty())
1298 par->params().labelString(layout.labelstring());
1300 par->params().labelString(string());
1302 if (!layout.labelstring_appendix().empty())
1303 par->params().labelString(layout.labelstring_appendix());
1305 par->params().labelString(string());
1310 if (!par->params().appendix()) {
1311 switch (2 * LABEL_COUNTER_CHAPTER -
1312 textclass.maxcounter() + i) {
1313 case LABEL_COUNTER_CHAPTER:
1314 s << par->getCounter(i);
1316 case LABEL_COUNTER_SECTION:
1317 s << par->getCounter(i - 1) << '.'
1318 << par->getCounter(i);
1320 case LABEL_COUNTER_SUBSECTION:
1321 s << par->getCounter(i - 2) << '.'
1322 << par->getCounter(i - 1) << '.'
1323 << par->getCounter(i);
1325 case LABEL_COUNTER_SUBSUBSECTION:
1326 s << par->getCounter(i - 3) << '.'
1327 << par->getCounter(i - 2) << '.'
1328 << par->getCounter(i - 1) << '.'
1329 << par->getCounter(i);
1332 case LABEL_COUNTER_PARAGRAPH:
1333 s << par->getCounter(i - 4) << '.'
1334 << par->getCounter(i - 3) << '.'
1335 << par->getCounter(i - 2) << '.'
1336 << par->getCounter(i - 1) << '.'
1337 << par->getCounter(i);
1339 case LABEL_COUNTER_SUBPARAGRAPH:
1340 s << par->getCounter(i - 5) << '.'
1341 << par->getCounter(i - 4) << '.'
1342 << par->getCounter(i - 3) << '.'
1343 << par->getCounter(i - 2) << '.'
1344 << par->getCounter(i - 1) << '.'
1345 << par->getCounter(i);
1349 // Can this ever be reached? And in the
1350 // case it is, how can this be correct?
1352 s << par->getCounter(i) << '.';
1355 } else { // appendix
1356 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1357 case LABEL_COUNTER_CHAPTER:
1358 if (par->isRightToLeftPar(buf->params))
1359 s << hebrewCounter(par->getCounter(i));
1361 s << alphaCounter(par->getCounter(i));
1363 case LABEL_COUNTER_SECTION:
1364 if (par->isRightToLeftPar(buf->params))
1365 s << hebrewCounter(par->getCounter(i - 1));
1367 s << alphaCounter(par->getCounter(i - 1));
1370 << par->getCounter(i);
1373 case LABEL_COUNTER_SUBSECTION:
1374 if (par->isRightToLeftPar(buf->params))
1375 s << hebrewCounter(par->getCounter(i - 2));
1377 s << alphaCounter(par->getCounter(i - 2));
1380 << par->getCounter(i-1) << '.'
1381 << par->getCounter(i);
1384 case LABEL_COUNTER_SUBSUBSECTION:
1385 if (par->isRightToLeftPar(buf->params))
1386 s << hebrewCounter(par->getCounter(i-3));
1388 s << alphaCounter(par->getCounter(i-3));
1391 << par->getCounter(i-2) << '.'
1392 << par->getCounter(i-1) << '.'
1393 << par->getCounter(i);
1396 case LABEL_COUNTER_PARAGRAPH:
1397 if (par->isRightToLeftPar(buf->params))
1398 s << hebrewCounter(par->getCounter(i-4));
1400 s << alphaCounter(par->getCounter(i-4));
1403 << par->getCounter(i-3) << '.'
1404 << par->getCounter(i-2) << '.'
1405 << par->getCounter(i-1) << '.'
1406 << par->getCounter(i);
1409 case LABEL_COUNTER_SUBPARAGRAPH:
1410 if (par->isRightToLeftPar(buf->params))
1411 s << hebrewCounter(par->getCounter(i-5));
1413 s << alphaCounter(par->getCounter(i-5));
1416 << par->getCounter(i-4) << '.'
1417 << par->getCounter(i-3) << '.'
1418 << par->getCounter(i-2) << '.'
1419 << par->getCounter(i-1) << '.'
1420 << par->getCounter(i);
1424 // Can this ever be reached? And in the
1425 // case it is, how can this be correct?
1427 s << par->getCounter(i) << '.';
1433 par->params().labelString(par->params().labelString() +s.str().c_str());
1434 // We really want to remove the c_str as soon as
1437 for (i++; i < 10; ++i) {
1438 // reset the following counters
1439 par->setCounter(i, 0);
1441 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1442 for (i++; i < 10; ++i) {
1443 // reset the following counters
1444 par->setCounter(i, 0);
1446 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1447 par->incCounter(i + par->enumdepth);
1448 int number = par->getCounter(i + par->enumdepth);
1452 switch (par->enumdepth) {
1454 if (par->isRightToLeftPar(buf->params))
1456 << hebrewCounter(number)
1460 << loweralphaCounter(number)
1464 if (par->isRightToLeftPar(buf->params))
1465 s << '.' << romanCounter(number);
1467 s << romanCounter(number) << '.';
1470 if (par->isRightToLeftPar(buf->params))
1472 << alphaCounter(number);
1474 s << alphaCounter(number)
1478 if (par->isRightToLeftPar(buf->params))
1485 par->params().labelString(s.str().c_str());
1487 for (i += par->enumdepth + 1; i < 10; ++i) {
1488 // reset the following counters
1489 par->setCounter(i, 0);
1493 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1494 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1496 int number = par->getCounter(i);
1498 InsetCommandParams p( "bibitem" );
1499 par->bibkey = new InsetBibKey(p);
1501 par->bibkey->setCounter(number);
1502 par->params().labelString(layout.labelstring());
1504 // In biblio should't be following counters but...
1506 string s = layout.labelstring();
1508 // the caption hack:
1509 if (layout.labeltype == LABEL_SENSITIVE) {
1510 bool isOK (par->inInset() && par->inInset()->owner() &&
1511 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1514 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1516 = floatList.getType(tmp->type());
1517 // We should get the correct number here too.
1518 s = fl.name() + " #:";
1520 /* par->SetLayout(0);
1521 s = layout->labelstring; */
1522 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1523 ? " :úåòîùî øñç" : "Senseless: ";
1526 par->params().labelString(s);
1528 /* reset the enumeration counter. They are always resetted
1529 * when there is any other layout between */
1530 for (int i = 6 + par->enumdepth; i < 10; ++i)
1531 par->setCounter(i, 0);
1536 // Updates all counters BEHIND the row. Changed paragraphs
1537 // with a dynamic left margin will be rebroken.
1538 void LyXText::updateCounters(BufferView * bview, Row * row) const
1546 par = row->par()->next();
1550 while (row->par() != par)
1553 setCounter(bview->buffer(), par);
1555 // now check for the headline layouts. remember that they
1556 // have a dynamic left margin
1557 if ((textclasslist.Style(bview->buffer()->params.textclass,
1558 par->layout).margintype == MARGIN_DYNAMIC
1559 || textclasslist.Style(bview->buffer()->params.textclass,
1560 par->layout).labeltype == LABEL_SENSITIVE)) {
1562 // Rebreak the paragraph
1563 removeParagraph(row);
1564 appendParagraph(bview, row);
1571 void LyXText::insertInset(BufferView * bview, Inset * inset)
1573 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1575 setUndo(bview, Undo::INSERT,
1576 cursor.par(), cursor.par()->next());
1577 cursor.par()->insertInset(cursor.pos(), inset);
1578 // Just to rebreak and refresh correctly.
1579 // The character will not be inserted a second time
1580 insertChar(bview, Paragraph::META_INSET);
1582 // If we enter a highly editable inset the cursor should be to before
1583 // the inset. This couldn't happen before as Undo was not handled inside
1584 // inset now after the Undo LyX tries to call inset->Edit(...) again
1585 // and cannot do this as the cursor is behind the inset and GetInset
1586 // does not return the inset!
1587 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1588 cursorLeft(bview, true);
1594 void LyXText::copyEnvironmentType()
1596 copylayouttype = cursor.par()->getLayout();
1600 void LyXText::pasteEnvironmentType(BufferView * bview)
1602 setLayout(bview, copylayouttype);
1606 void LyXText::cutSelection(BufferView * bview, bool doclear)
1608 // Stuff what we got on the clipboard. Even if there is no selection.
1610 // There is a problem with having the stuffing here in that the
1611 // larger the selection the slower LyX will get. This can be
1612 // solved by running the line below only when the selection has
1613 // finished. The solution used currently just works, to make it
1614 // faster we need to be more clever and probably also have more
1615 // calls to stuffClipboard. (Lgb)
1616 bview->stuffClipboard(selectionAsString(bview->buffer()));
1618 // This doesn't make sense, if there is no selection
1619 if (!selection.set())
1622 // OK, we have a selection. This is always between selection.start
1623 // and selection.end
1625 // make sure that the depth behind the selection are restored, too
1626 Paragraph * endpar = selection.end.par()->next();
1627 Paragraph * undoendpar = endpar;
1629 if (endpar && endpar->getDepth()) {
1630 while (endpar && endpar->getDepth()) {
1631 endpar = endpar->next();
1632 undoendpar = endpar;
1634 } else if (endpar) {
1635 endpar = endpar->next(); // because of parindents etc.
1638 setUndo(bview, Undo::DELETE,
1639 selection.start.par(), undoendpar);
1641 // there are two cases: cut only within one paragraph or
1642 // more than one paragraph
1643 if (selection.start.par() == selection.end.par()) {
1644 // only within one paragraph
1645 endpar = selection.end.par();
1646 int pos = selection.end.pos();
1647 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1648 selection.start.pos(), pos,
1649 bview->buffer()->params.textclass, doclear);
1650 selection.end.pos(pos);
1652 endpar = selection.end.par();
1653 int pos = selection.end.pos();
1654 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1655 selection.start.pos(), pos,
1656 bview->buffer()->params.textclass, doclear);
1658 selection.end.par(endpar);
1659 selection.end.pos(pos);
1660 cursor.pos(selection.end.pos());
1662 endpar = endpar->next();
1664 // sometimes necessary
1666 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1668 redoParagraphs(bview, selection.start, endpar);
1670 // cutSelection can invalidate the cursor so we need to set
1672 cursor = selection.start;
1674 // need a valid cursor. (Lgb)
1675 clearSelection(bview);
1677 setCursor(bview, cursor.par(), cursor.pos());
1678 selection.cursor = cursor;
1679 updateCounters(bview, cursor.row());
1683 void LyXText::copySelection(BufferView * bview)
1685 // Stuff what we got on the clipboard. Even if there is no selection.
1687 // There is a problem with having the stuffing here in that the
1688 // larger the selection the slower LyX will get. This can be
1689 // solved by running the line below only when the selection has
1690 // finished. The solution used currently just works, to make it
1691 // faster we need to be more clever and probably also have more
1692 // calls to stuffClipboard. (Lgb)
1693 bview->stuffClipboard(selectionAsString(bview->buffer()));
1695 // this doesnt make sense, if there is no selection
1696 if (!selection.set())
1699 // ok we have a selection. This is always between selection.start
1700 // and sel_end cursor
1702 // copy behind a space if there is one
1703 while (selection.start.par()->size() > selection.start.pos()
1704 && selection.start.par()->isLineSeparator(selection.start.pos())
1705 && (selection.start.par() != selection.end.par()
1706 || selection.start.pos() < selection.end.pos()))
1707 selection.start.pos(selection.start.pos() + 1);
1709 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1710 selection.start.pos(), selection.end.pos(),
1711 bview->buffer()->params.textclass);
1715 void LyXText::pasteSelection(BufferView * bview)
1717 // this does not make sense, if there is nothing to paste
1718 if (!CutAndPaste::checkPastePossible(cursor.par()))
1721 setUndo(bview, Undo::INSERT,
1722 cursor.par(), cursor.par()->next());
1725 Paragraph * actpar = cursor.par();
1727 int pos = cursor.pos();
1728 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1729 bview->buffer()->params.textclass);
1731 redoParagraphs(bview, cursor, endpar);
1733 setCursor(bview, cursor.par(), cursor.pos());
1734 clearSelection(bview);
1736 selection.cursor = cursor;
1737 setCursor(bview, actpar, pos);
1738 setSelection(bview);
1739 updateCounters(bview, cursor.row());
1743 // returns a pointer to the very first Paragraph
1744 Paragraph * LyXText::firstParagraph() const
1746 return ownerParagraph();
1750 // sets the selection over the number of characters of string, no check!!
1751 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1756 selection.cursor = cursor;
1757 for (string::size_type i = 0; i < str.length(); ++i)
1759 setSelection(bview);
1763 // simple replacing. The font of the first selected character is used
1764 void LyXText::replaceSelectionWithString(BufferView * bview,
1767 setCursorParUndo(bview);
1770 if (!selection.set()) { // create a dummy selection
1771 selection.end = cursor;
1772 selection.start = cursor;
1775 // Get font setting before we cut
1776 Paragraph::size_type pos = selection.end.pos();
1777 LyXFont const font = selection.start.par()
1778 ->getFontSettings(bview->buffer()->params,
1779 selection.start.pos());
1781 // Insert the new string
1782 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1783 selection.end.par()->insertChar(pos, (*cit), font);
1787 // Cut the selection
1788 cutSelection(bview);
1794 // needed to insert the selection
1795 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1797 Paragraph * par = cursor.par();
1798 Paragraph::size_type pos = cursor.pos();
1799 Paragraph * endpar = cursor.par()->next();
1801 setCursorParUndo(bview);
1803 // only to be sure, should not be neccessary
1804 clearSelection(bview);
1806 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1808 redoParagraphs(bview, cursor, endpar);
1809 setCursor(bview, cursor.par(), cursor.pos());
1810 selection.cursor = cursor;
1811 setCursor(bview, par, pos);
1812 setSelection(bview);
1816 // turns double-CR to single CR, others where converted into one
1817 // blank. Then InsertStringAsLines is called
1818 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1820 string linestr(str);
1821 bool newline_inserted = false;
1822 for (string::size_type i = 0; i < linestr.length(); ++i) {
1823 if (linestr[i] == '\n') {
1824 if (newline_inserted) {
1825 // we know that \r will be ignored by
1826 // InsertStringA. Of course, it is a dirty
1827 // trick, but it works...
1828 linestr[i - 1] = '\r';
1832 newline_inserted = true;
1834 } else if (IsPrintable(linestr[i])) {
1835 newline_inserted = false;
1838 insertStringAsLines(bview, linestr);
1842 bool LyXText::gotoNextInset(BufferView * bview,
1843 std::vector<Inset::Code> const & codes,
1844 string const & contents) const
1846 LyXCursor res = cursor;
1849 if (res.pos() < res.par()->size() - 1) {
1850 res.pos(res.pos() + 1);
1852 res.par(res.par()->next());
1856 } while (res.par() &&
1857 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1858 && (inset = res.par()->getInset(res.pos())) != 0
1859 && find(codes.begin(), codes.end(), inset->lyxCode())
1861 && (contents.empty() ||
1862 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1866 setCursor(bview, res.par(), res.pos());
1873 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1874 Paragraph::size_type pos)
1876 LyXCursor tmpcursor;
1879 Paragraph::size_type z;
1880 Row * row = getRow(par, pos, y);
1882 // is there a break one row above
1883 if (row->previous() && row->previous()->par() == row->par()) {
1884 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1885 if (z >= row->pos()) {
1886 // set the dimensions of the row above
1887 y -= row->previous()->height();
1889 refresh_row = row->previous();
1890 status(bview, LyXText::NEED_MORE_REFRESH);
1892 breakAgain(bview, row->previous());
1894 // set the cursor again. Otherwise
1895 // dangling pointers are possible
1896 setCursor(bview, cursor.par(), cursor.pos(),
1897 false, cursor.boundary());
1898 selection.cursor = cursor;
1903 int const tmpheight = row->height();
1904 Paragraph::size_type const tmplast = rowLast(row);
1908 breakAgain(bview, row);
1909 if (row->height() == tmpheight && rowLast(row) == tmplast)
1910 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1912 status(bview, LyXText::NEED_MORE_REFRESH);
1914 // check the special right address boxes
1915 if (textclasslist.Style(bview->buffer()->params.textclass,
1916 par->getLayout()).margintype
1917 == MARGIN_RIGHT_ADDRESS_BOX) {
1924 redoDrawingOfParagraph(bview, tmpcursor);
1927 // set the cursor again. Otherwise dangling pointers are possible
1928 // also set the selection
1930 if (selection.set()) {
1932 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1933 false, selection.cursor.boundary());
1934 selection.cursor = cursor;
1935 setCursorIntern(bview, selection.start.par(),
1936 selection.start.pos(),
1937 false, selection.start.boundary());
1938 selection.start = cursor;
1939 setCursorIntern(bview, selection.end.par(),
1940 selection.end.pos(),
1941 false, selection.end.boundary());
1942 selection.end = cursor;
1943 setCursorIntern(bview, last_sel_cursor.par(),
1944 last_sel_cursor.pos(),
1945 false, last_sel_cursor.boundary());
1946 last_sel_cursor = cursor;
1949 setCursorIntern(bview, cursor.par(), cursor.pos(),
1950 false, cursor.boundary());
1954 // returns false if inset wasn't found
1955 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1957 // first check the current paragraph
1958 int pos = cursor.par()->getPositionOfInset(inset);
1960 checkParagraph(bview, cursor.par(), pos);
1964 // check every paragraph
1966 Paragraph * par = firstParagraph();
1968 pos = par->getPositionOfInset(inset);
1970 checkParagraph(bview, par, pos);
1980 void LyXText::setCursor(BufferView * bview, Paragraph * par,
1981 Paragraph::size_type pos,
1982 bool setfont, bool boundary) const
1984 LyXCursor old_cursor = cursor;
1985 setCursorIntern(bview, par, pos, setfont, boundary);
1986 deleteEmptyParagraphMechanism(bview, old_cursor);
1990 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
1991 Paragraph::size_type pos, bool boundary) const
1995 cur.boundary(boundary);
1997 // get the cursor y position in text
1999 Row * row = getRow(par, pos, y);
2000 // y is now the beginning of the cursor row
2001 y += row->baseline();
2002 // y is now the cursor baseline
2005 // now get the cursors x position
2007 float fill_separator;
2009 float fill_label_hfill;
2010 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2012 Paragraph::size_type cursor_vpos = 0;
2013 Paragraph::size_type last = rowLastPrintable(row);
2015 if (pos > last + 1) // This shouldn't happen.
2017 else if (pos < row->pos())
2020 if (last < row->pos())
2021 cursor_vpos = row->pos();
2022 else if (pos > last && !boundary)
2023 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2024 ? row->pos() : last + 1;
2025 else if (pos > row->pos() &&
2026 (pos > last || boundary))
2027 /// Place cursor after char at (logical) position pos - 1
2028 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2029 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2031 /// Place cursor before char at (logical) position pos
2032 cursor_vpos = (bidi_level(pos) % 2 == 0)
2033 ? log2vis(pos) : log2vis(pos) + 1;
2035 Paragraph::size_type main_body =
2036 beginningOfMainBody(bview->buffer(), row->par());
2037 if ((main_body > 0) &&
2038 ((main_body-1 > last) ||
2039 !row->par()->isLineSeparator(main_body-1)))
2042 for (Paragraph::size_type vpos = row->pos();
2043 vpos < cursor_vpos; ++vpos) {
2044 pos = vis2log(vpos);
2045 if (main_body > 0 && pos == main_body - 1) {
2046 x += fill_label_hfill +
2047 lyxfont::width(textclasslist.Style(
2048 bview->buffer()->params.textclass,
2049 row->par()->getLayout())
2051 getFont(bview->buffer(), row->par(), -2));
2052 if (row->par()->isLineSeparator(main_body-1))
2053 x -= singleWidth(bview, row->par(),main_body-1);
2055 if (hfillExpansion(bview->buffer(), row, pos)) {
2056 x += singleWidth(bview, row->par(), pos);
2057 if (pos >= main_body)
2060 x += fill_label_hfill;
2061 } else if (row->par()->isSeparator(pos)) {
2062 x += singleWidth(bview, row->par(), pos);
2063 if (pos >= main_body)
2064 x += fill_separator;
2066 x += singleWidth(bview, row->par(), pos);
2075 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2076 Paragraph::size_type pos,
2077 bool setfont, bool boundary) const
2079 InsetText * it = static_cast<InsetText *>(par->inInset());
2080 if (it && (it != inset_owner)) {
2081 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2084 setCursor(bview, cursor, par, pos, boundary);
2086 setCurrentFont(bview);
2091 void LyXText::setCurrentFont(BufferView * bview) const
2093 Paragraph::size_type pos = cursor.pos();
2094 if (cursor.boundary() && pos > 0)
2098 if (pos == cursor.par()->size())
2100 else // potentional bug... BUG (Lgb)
2101 if (cursor.par()->isSeparator(pos)) {
2102 if (pos > cursor.row()->pos() &&
2103 bidi_level(pos) % 2 ==
2104 bidi_level(pos - 1) % 2)
2106 else if (pos + 1 < cursor.par()->size())
2112 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2113 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2115 if (cursor.pos() == cursor.par()->size() &&
2116 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2117 !cursor.boundary()) {
2118 Language const * lang =
2119 cursor.par()->getParLanguage(bview->buffer()->params);
2120 current_font.setLanguage(lang);
2121 current_font.setNumber(LyXFont::OFF);
2122 real_current_font.setLanguage(lang);
2123 real_current_font.setNumber(LyXFont::OFF);
2128 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2130 LyXCursor old_cursor = cursor;
2132 // Get the row first.
2134 Row * row = getRowNearY(y);
2135 cursor.par(row->par());
2138 int column = getColumnNearX(bview, row, x, bound);
2139 cursor.pos(row->pos() + column);
2141 cursor.y(y + row->baseline());
2143 cursor.boundary(bound);
2144 setCurrentFont(bview);
2145 deleteEmptyParagraphMechanism(bview, old_cursor);
2149 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2152 // Get the row first.
2154 Row * row = getRowNearY(y);
2156 int column = getColumnNearX(bview, row, x, bound);
2158 cur.par(row->par());
2159 cur.pos(row->pos() + column);
2161 cur.y(y + row->baseline());
2163 cur.boundary(bound);
2167 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2169 if (cursor.pos() > 0) {
2170 bool boundary = cursor.boundary();
2171 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2172 if (!internal && !boundary &&
2173 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2174 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2175 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2176 Paragraph * par = cursor.par()->previous();
2177 setCursor(bview, par, par->size());
2182 void LyXText::cursorRight(BufferView * bview, bool internal) const
2184 if (!internal && cursor.boundary() &&
2185 !cursor.par()->isNewline(cursor.pos()))
2186 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2187 else if (cursor.pos() < cursor.par()->size()) {
2188 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2190 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2191 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2192 } else if (cursor.par()->next())
2193 setCursor(bview, cursor.par()->next(), 0);
2197 void LyXText::cursorUp(BufferView * bview) const
2199 setCursorFromCoordinates(bview, cursor.x_fix(),
2200 cursor.y() - cursor.row()->baseline() - 1);
2204 void LyXText::cursorDown(BufferView * bview) const
2206 setCursorFromCoordinates(bview, cursor.x_fix(),
2207 cursor.y() - cursor.row()->baseline()
2208 + cursor.row()->height() + 1);
2212 void LyXText::cursorUpParagraph(BufferView * bview) const
2214 if (cursor.pos() > 0) {
2215 setCursor(bview, cursor.par(), 0);
2217 else if (cursor.par()->previous()) {
2218 setCursor(bview, cursor.par()->previous(), 0);
2223 void LyXText::cursorDownParagraph(BufferView * bview) const
2225 if (cursor.par()->next()) {
2226 setCursor(bview, cursor.par()->next(), 0);
2228 setCursor(bview, cursor.par(), cursor.par()->size());
2233 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2234 LyXCursor const & old_cursor) const
2236 // Would be wrong to delete anything if we have a selection.
2237 if (selection.set()) return;
2239 // We allow all kinds of "mumbo-jumbo" when freespacing.
2240 if (textclasslist.Style(bview->buffer()->params.textclass,
2241 old_cursor.par()->getLayout()).free_spacing)
2244 bool deleted = false;
2246 /* Ok I'll put some comments here about what is missing.
2247 I have fixed BackSpace (and thus Delete) to not delete
2248 double-spaces automagically. I have also changed Cut,
2249 Copy and Paste to hopefully do some sensible things.
2250 There are still some small problems that can lead to
2251 double spaces stored in the document file or space at
2252 the beginning of paragraphs. This happens if you have
2253 the cursor betwenn to spaces and then save. Or if you
2254 cut and paste and the selection have a space at the
2255 beginning and then save right after the paste. I am
2256 sure none of these are very hard to fix, but I will
2257 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2258 that I can get some feedback. (Lgb)
2261 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2262 // delete the LineSeparator.
2265 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2266 // delete the LineSeparator.
2269 // If the pos around the old_cursor were spaces, delete one of them.
2270 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2271 // Only if the cursor has really moved
2273 if (old_cursor.pos() > 0
2274 && old_cursor.pos() < old_cursor.par()->size()
2275 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2276 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2277 old_cursor.par()->erase(old_cursor.pos() - 1);
2278 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2280 if (old_cursor.par() == cursor.par() &&
2281 cursor.pos() > old_cursor.pos()) {
2282 setCursorIntern(bview, cursor.par(),
2285 setCursorIntern(bview, cursor.par(),
2291 // Do not delete empty paragraphs with keepempty set.
2292 if ((textclasslist.Style(bview->buffer()->params.textclass,
2293 old_cursor.par()->getLayout())).keepempty)
2296 LyXCursor tmpcursor;
2298 if (old_cursor.par() != cursor.par()) {
2299 if ((old_cursor.par()->size() == 0
2300 || (old_cursor.par()->size() == 1
2301 && old_cursor.par()->isLineSeparator(0)))) {
2302 // ok, we will delete anything
2304 // make sure that you do not delete any environments
2305 status(bview, LyXText::NEED_MORE_REFRESH);
2308 if (old_cursor.row()->previous()) {
2309 refresh_row = old_cursor.row()->previous();
2310 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2312 cursor = old_cursor; // that undo can restore the right cursor position
2313 Paragraph * endpar = old_cursor.par()->next();
2314 if (endpar && endpar->getDepth()) {
2315 while (endpar && endpar->getDepth()) {
2316 endpar = endpar->next();
2319 setUndo(bview, Undo::DELETE,
2325 removeRow(old_cursor.row());
2326 if (ownerParagraph() == old_cursor.par()) {
2327 ownerParagraph(ownerParagraph()->next());
2330 delete old_cursor.par();
2332 /* Breakagain the next par. Needed
2333 * because of the parindent that
2334 * can occur or dissappear. The
2335 * next row can change its height,
2336 * if there is another layout before */
2337 if (refresh_row->next()) {
2338 breakAgain(bview, refresh_row->next());
2339 updateCounters(bview, refresh_row);
2341 setHeightOfRow(bview, refresh_row);
2343 refresh_row = old_cursor.row()->next();
2344 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2347 cursor = old_cursor; // that undo can restore the right cursor position
2348 Paragraph * endpar = old_cursor.par()->next();
2349 if (endpar && endpar->getDepth()) {
2350 while (endpar && endpar->getDepth()) {
2351 endpar = endpar->next();
2354 setUndo(bview, Undo::DELETE,
2360 removeRow(old_cursor.row());
2362 if (ownerParagraph() == old_cursor.par()) {
2363 ownerParagraph(ownerParagraph()->next());
2366 delete old_cursor.par();
2368 /* Breakagain the next par. Needed
2369 because of the parindent that can
2370 occur or dissappear.
2371 The next row can change its height,
2372 if there is another layout before
2375 breakAgain(bview, refresh_row);
2376 updateCounters(bview, refresh_row->previous());
2382 setCursorIntern(bview, cursor.par(), cursor.pos());
2384 if (selection.cursor.par() == old_cursor.par()
2385 && selection.cursor.pos() == selection.cursor.pos()) {
2386 // correct selection
2387 selection.cursor = cursor;
2391 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2392 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2394 setCursorIntern(bview, cursor.par(), cursor.pos());
2395 selection.cursor = cursor;
2402 void LyXText::toggleAppendix(BufferView * bview)
2404 Paragraph * par = cursor.par();
2405 bool start = !par->params().startOfAppendix();
2407 // ensure that we have only one start_of_appendix in this document
2408 Paragraph * tmp = firstParagraph();
2409 for (; tmp; tmp = tmp->next()) {
2410 tmp->params().startOfAppendix(false);
2413 par->params().startOfAppendix(start);
2415 // we can set the refreshing parameters now
2416 status(bview, LyXText::NEED_MORE_REFRESH);
2418 refresh_row = 0; // not needed for full update
2419 updateCounters(bview, 0);
2420 setCursor(bview, cursor.par(), cursor.pos());
2424 Paragraph * LyXText::ownerParagraph() const
2427 return inset_owner->paragraph();
2429 return bv_owner->buffer()->paragraph;
2433 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2436 inset_owner->paragraph(p);
2438 bv_owner->buffer()->paragraph = p;
2443 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2445 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2446 if (op && op->inInset()) {
2447 static_cast<InsetText *>(op->inInset())->paragraph(p);
2450 inset_owner->paragraph(p);
2452 bv_owner->buffer()->paragraph = p;
2459 LyXText::text_status LyXText::status() const
2465 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2468 if ((status_ != NEED_MORE_REFRESH)
2469 || (status_ == NEED_MORE_REFRESH)
2470 && (st != NEED_VERY_LITTLE_REFRESH)) {
2472 if (inset_owner && st != UNCHANGED) {
2473 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2477 #warning Please tell what the intention is here. (Lgb)
2478 // The above does not make any sense, I changed it to what is here,
2479 // but it still does not make much sense. (Lgb)
2480 #warning Sure have a look now! (Jug)
2481 // well as much as I know && binds more then || so the above and the
2482 // below are identical (this for your known use of parentesis!)
2483 // Now some explanation:
2484 // We should only go up with refreshing code so this means that if
2485 // we have a MORE refresh we should never set it to LITTLE if we still
2486 // didn't handle it (and then it will be UNCHANGED. Now as long as
2487 // we stay inside one LyXText this may work but we need to tell the
2488 // outermost LyXText that it should REALLY draw us if there is some
2489 // change in a Inset::LyXText. So you see that when we are inside a
2490 // inset's LyXText we give the LITTLE to the outermost LyXText to
2491 // tell'em that it should redraw the actual row (where the inset
2492 // resides! Capito?!
2494 if ((status_ != NEED_MORE_REFRESH)
2495 || (status_ == NEED_MORE_REFRESH
2496 && st != NEED_VERY_LITTLE_REFRESH))
2499 if (inset_owner && st != UNCHANGED) {
2500 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);