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)
73 Paragraph * par = ownerParagraph();
74 current_font = getFont(bview->buffer(), par, 0);
76 insertParagraph(bview, par, lastrow);
79 setCursorIntern(bview, firstrow->par(), 0);
80 selection.cursor = cursor;
86 // Delete all rows, this does not touch the paragraphs!
87 Row * tmprow = firstrow;
89 tmprow = firstrow->next();
96 // Gets the fully instantiated font at a given position in a paragraph
97 // Basically the same routine as Paragraph::getFont() in paragraph.C.
98 // The difference is that this one is used for displaying, and thus we
99 // are allowed to make cosmetic improvements. For instance make footnotes
101 // If position is -1, we get the layout font of the paragraph.
102 // If position is -2, we get the font of the manual label of the paragraph.
103 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
104 Paragraph::size_type pos) const
106 LyXLayout const & layout =
107 textclasslist.Style(buf->params.textclass, par->getLayout());
109 Paragraph::depth_type par_depth = par->getDepth();
110 // We specialize the 95% common case:
114 if (layout.labeltype == LABEL_MANUAL
115 && pos < beginningOfMainBody(buf, par)) {
117 LyXFont f = par->getFontSettings(buf->params,
119 return f.realize(layout.reslabelfont);
121 LyXFont f = par->getFontSettings(buf->params, pos);
122 return f.realize(layout.resfont);
127 // process layoutfont for pos == -1 and labelfont for pos < -1
129 return layout.resfont;
131 return layout.reslabelfont;
135 // The uncommon case need not be optimized as much
137 LyXFont layoutfont, tmpfont;
141 if (pos < beginningOfMainBody(buf, par)) {
143 layoutfont = layout.labelfont;
146 layoutfont = layout.font;
148 tmpfont = par->getFontSettings(buf->params, pos);
149 tmpfont.realize(layoutfont);
152 // process layoutfont for pos == -1 and labelfont for pos < -1
154 tmpfont = layout.font;
156 tmpfont = layout.labelfont;
159 // Resolve against environment font information
160 while (par && par_depth && !tmpfont.resolved()) {
161 par = par->outerHook();
163 tmpfont.realize(textclasslist.
164 Style(buf->params.textclass,
165 par->getLayout()).font);
166 par_depth = par->getDepth();
170 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
176 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
177 Paragraph::size_type pos, LyXFont const & fnt,
180 Buffer const * buf = bv->buffer();
181 LyXFont font = getFont(buf, par, pos);
182 font.update(fnt, buf->params.language, toggleall);
183 // Let the insets convert their font
184 if (par->getChar(pos) == Paragraph::META_INSET) {
185 Inset * inset = par->getInset(pos);
187 if (inset->editable()==Inset::HIGHLY_EDITABLE) {
188 UpdatableInset * uinset =
189 static_cast<UpdatableInset *>(inset);
190 uinset->setFont(bv, fnt, toggleall, true);
192 font = inset->convertFont(font);
196 LyXLayout const & layout =
197 textclasslist.Style(buf->params.textclass,
200 // Get concrete layout font to reduce against
203 if (pos < beginningOfMainBody(buf, par))
204 layoutfont = layout.labelfont;
206 layoutfont = layout.font;
208 // Realize against environment font information
209 if (par->getDepth()){
210 Paragraph * tp = par;
211 while (!layoutfont.resolved() && tp && tp->getDepth()) {
212 tp = tp->outerHook();
214 layoutfont.realize(textclasslist.
215 Style(buf->params.textclass,
216 tp->getLayout()).font);
220 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
222 // Now, reduce font against full layout font
223 font.reduce(layoutfont);
225 par->setFont(pos, font);
229 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
230 Paragraph::size_type pos, LyXFont const & fnt)
233 // Let the insets convert their font
234 if (par->getChar(pos) == Paragraph::META_INSET) {
235 font = par->getInset(pos)->convertFont(font);
238 LyXLayout const & layout =
239 textclasslist.Style(buf->params.textclass,
242 // Get concrete layout font to reduce against
245 if (pos < beginningOfMainBody(buf, par))
246 layoutfont = layout.labelfont;
248 layoutfont = layout.font;
250 // Realize against environment font information
251 if (par->getDepth()){
252 Paragraph * tp = par;
253 while (!layoutfont.resolved() && tp && tp->getDepth()) {
254 tp = tp->outerHook();
256 layoutfont.realize(textclasslist.
257 Style(buf->params.textclass,
258 tp->getLayout()).font);
262 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
264 // Now, reduce font against full layout font
265 font.reduce(layoutfont);
267 par->setFont(pos, font);
271 // inserts a new row behind the specified row, increments
272 // the touched counters
273 void LyXText::insertRow(Row * row, Paragraph * par,
274 Paragraph::size_type pos) const
276 Row * tmprow = new Row;
279 tmprow->next(firstrow);
282 tmprow->previous(row);
283 tmprow->next(row->next());
288 tmprow->next()->previous(tmprow);
290 if (tmprow->previous())
291 tmprow->previous()->next(tmprow);
303 // removes the row and reset the touched counters
304 void LyXText::removeRow(Row * row) const
306 /* this must not happen before the currentrow for clear reasons.
307 so the trick is just to set the current row onto the previous
310 getRow(row->par(), row->pos(), unused_y);
313 row->next()->previous(row->previous());
314 if (!row->previous()) {
315 firstrow = row->next();
317 row->previous()->next(row->next());
320 lastrow = row->previous();
322 height -= row->height(); // the text becomes smaller
325 --number_of_rows; // one row less
329 // remove all following rows of the paragraph of the specified row.
330 void LyXText::removeParagraph(Row * row) const
332 Paragraph * tmppar = row->par();
336 while (row && row->par() == tmppar) {
337 tmprow = row->next();
344 // insert the specified paragraph behind the specified row
345 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
348 insertRow(row, par, 0); /* insert a new row, starting
351 setCounter(bview->buffer(), par); // set the counters
353 // and now append the whole paragraph behind the new row
356 appendParagraph(bview, firstrow);
358 row->next()->height(0);
359 appendParagraph(bview, row->next());
363 Inset * LyXText::getInset() const
366 if (cursor.pos() == 0 && cursor.par()->bibkey){
367 inset = cursor.par()->bibkey;
368 } else if (cursor.pos() < cursor.par()->size()
369 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
370 inset = cursor.par()->getInset(cursor.pos());
375 void LyXText::toggleInset(BufferView * bview)
377 Inset * inset = getInset();
378 if (!inset->editable())
380 //bview->owner()->message(inset->editMessage());
382 // do we want to keep this?? (JMarc)
383 if (inset->editable() != Inset::HIGHLY_EDITABLE)
384 setCursorParUndo(bview);
385 inset->open(bview, !inset->isOpen());
389 /* used in setlayout */
390 // Asger is not sure we want to do this...
391 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
394 LyXLayout const & layout =
395 textclasslist.Style(buf->params.textclass, par->getLayout());
397 LyXFont layoutfont, tmpfont;
398 for (Paragraph::size_type pos = 0;
399 pos < par->size(); ++pos) {
400 if (pos < beginningOfMainBody(buf, par))
401 layoutfont = layout.labelfont;
403 layoutfont = layout.font;
405 tmpfont = par->getFontSettings(buf->params, pos);
406 tmpfont.reduce(layoutfont);
407 par->setFont(pos, tmpfont);
412 Paragraph * LyXText::setLayout(BufferView * bview,
413 LyXCursor & cur, LyXCursor & sstart_cur,
414 LyXCursor & send_cur,
415 LyXTextClass::size_type layout)
417 Paragraph * endpar = send_cur.par()->next();
418 Paragraph * undoendpar = endpar;
420 if (endpar && endpar->getDepth()) {
421 while (endpar && endpar->getDepth()) {
422 endpar = endpar->next();
426 endpar = endpar->next(); // because of parindents etc.
429 setUndo(bview, Undo::EDIT,
430 sstart_cur.par(), undoendpar);
432 // ok we have a selection. This is always between sstart_cur
433 // and sel_end cursor
436 LyXLayout const & lyxlayout =
437 textclasslist.Style(bview->buffer()->params.textclass, layout);
439 while (cur.par() != send_cur.par()) {
440 cur.par()->setLayout(layout);
441 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
442 Paragraph * fppar = cur.par();
443 fppar->params().spaceTop(lyxlayout.fill_top ?
444 VSpace(VSpace::VFILL)
445 : VSpace(VSpace::NONE));
446 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
447 VSpace(VSpace::VFILL)
448 : VSpace(VSpace::NONE));
449 if (lyxlayout.margintype == MARGIN_MANUAL)
450 cur.par()->setLabelWidthString(lyxlayout.labelstring());
451 if (lyxlayout.labeltype != LABEL_BIBLIO
453 delete fppar->bibkey;
456 cur.par(cur.par()->next());
458 cur.par()->setLayout(layout);
459 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
460 Paragraph * fppar = cur.par();
461 fppar->params().spaceTop(lyxlayout.fill_top ?
462 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
463 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
464 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
465 if (lyxlayout.margintype == MARGIN_MANUAL)
466 cur.par()->setLabelWidthString(lyxlayout.labelstring());
467 if (lyxlayout.labeltype != LABEL_BIBLIO
469 delete fppar->bibkey;
476 // set layout over selection and make a total rebreak of those paragraphs
477 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
479 LyXCursor tmpcursor = cursor; /* store the current cursor */
481 // if there is no selection just set the layout
482 // of the current paragraph */
483 if (!selection.set()) {
484 selection.start = cursor; // dummy selection
485 selection.end = cursor;
487 Paragraph * endpar = setLayout(bview, cursor, selection.start,
488 selection.end, layout);
489 redoParagraphs(bview, selection.start, endpar);
491 // we have to reset the selection, because the
492 // geometry could have changed
493 setCursor(bview, selection.start.par(),
494 selection.start.pos(), false);
495 selection.cursor = cursor;
496 setCursor(bview, selection.end.par(), selection.end.pos(), false);
497 updateCounters(bview, cursor.row());
498 clearSelection(bview);
500 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
504 // increment depth over selection and
505 // make a total rebreak of those paragraphs
506 void LyXText::incDepth(BufferView * bview)
508 // If there is no selection, just use the current paragraph
509 if (!selection.set()) {
510 selection.start = cursor; // dummy selection
511 selection.end = cursor;
514 // We end at the next paragraph with depth 0
515 Paragraph * endpar = selection.end.par()->next();
517 Paragraph * undoendpar = endpar;
519 if (endpar && endpar->getDepth()) {
520 while (endpar && endpar->getDepth()) {
521 endpar = endpar->next();
525 endpar = endpar->next(); // because of parindents etc.
528 setUndo(bview, Undo::EDIT,
529 selection.start.par(), undoendpar);
531 LyXCursor tmpcursor = cursor; // store the current cursor
533 // ok we have a selection. This is always between sel_start_cursor
534 // and sel_end cursor
535 cursor = selection.start;
537 bool anything_changed = false;
540 // NOTE: you can't change the depth of a bibliography entry
542 textclasslist.Style(bview->buffer()->params.textclass,
543 cursor.par()->getLayout()
544 ).labeltype != LABEL_BIBLIO) {
545 Paragraph * prev = cursor.par()->previous();
548 && (prev->getDepth() - cursor.par()->getDepth() > 0
549 || (prev->getDepth() == cursor.par()->getDepth()
550 && textclasslist.Style(bview->buffer()->params.textclass,
551 prev->getLayout()).isEnvironment()))) {
552 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
553 anything_changed = true;
556 if (cursor.par() == selection.end.par())
558 cursor.par(cursor.par()->next());
561 // if nothing changed set all depth to 0
562 if (!anything_changed) {
563 cursor = selection.start;
564 while (cursor.par() != selection.end.par()) {
565 cursor.par()->params().depth(0);
566 cursor.par(cursor.par()->next());
568 cursor.par()->params().depth(0);
571 redoParagraphs(bview, selection.start, endpar);
573 // we have to reset the selection, because the
574 // geometry could have changed
575 setCursor(bview, selection.start.par(), selection.start.pos());
576 selection.cursor = cursor;
577 setCursor(bview, selection.end.par(), selection.end.pos());
578 updateCounters(bview, cursor.row());
579 clearSelection(bview);
581 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
585 // decrement depth over selection and
586 // make a total rebreak of those paragraphs
587 void LyXText::decDepth(BufferView * bview)
589 // if there is no selection just set the layout
590 // of the current paragraph
591 if (!selection.set()) {
592 selection.start = cursor; // dummy selection
593 selection.end = cursor;
595 Paragraph * endpar = selection.end.par()->next();
596 Paragraph * undoendpar = endpar;
598 if (endpar && endpar->getDepth()) {
599 while (endpar && endpar->getDepth()) {
600 endpar = endpar->next();
604 endpar = endpar->next(); // because of parindents etc.
607 setUndo(bview, Undo::EDIT,
608 selection.start.par(), undoendpar);
610 LyXCursor tmpcursor = cursor; // store the current cursor
612 // ok we have a selection. This is always between sel_start_cursor
613 // and sel_end cursor
614 cursor = selection.start;
617 if (cursor.par()->params().depth()) {
618 cursor.par()->params()
619 .depth(cursor.par()->params().depth() - 1);
621 if (cursor.par() == selection.end.par()) {
624 cursor.par(cursor.par()->next());
627 redoParagraphs(bview, selection.start, endpar);
629 // we have to reset the selection, because the
630 // geometry could have changed
631 setCursor(bview, selection.start.par(),
632 selection.start.pos());
633 selection.cursor = cursor;
634 setCursor(bview, selection.end.par(), selection.end.pos());
635 updateCounters(bview, cursor.row());
636 clearSelection(bview);
638 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
642 // set font over selection and make a total rebreak of those paragraphs
643 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
645 // if there is no selection just set the current_font
646 if (!selection.set()) {
647 // Determine basis font
649 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
651 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
653 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
654 // Update current font
655 real_current_font.update(font,
656 bview->buffer()->params.language,
659 // Reduce to implicit settings
660 current_font = real_current_font;
661 current_font.reduce(layoutfont);
662 // And resolve it completely
663 real_current_font.realize(layoutfont);
667 LyXCursor tmpcursor = cursor; // store the current cursor
669 // ok we have a selection. This is always between sel_start_cursor
670 // and sel_end cursor
672 setUndo(bview, Undo::EDIT,
673 selection.start.par(),
674 selection.end.par()->next());
676 cursor = selection.start;
677 while (cursor.par() != selection.end.par() ||
678 (cursor.pos() < selection.end.pos())) {
679 if (cursor.pos() < cursor.par()->size()) {
680 // an open footnote should behave
682 setCharFont(bview, cursor.par(), cursor.pos(),
684 cursor.pos(cursor.pos() + 1);
687 cursor.par(cursor.par()->next());
692 redoParagraphs(bview, selection.start, selection.end.par()->next());
694 // we have to reset the selection, because the
695 // geometry could have changed
696 setCursor(bview, selection.start.par(), selection.start.pos());
697 selection.cursor = cursor;
698 setCursor(bview, selection.end.par(), selection.end.pos());
699 clearSelection(bview);
701 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
702 tmpcursor.boundary());
706 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
708 Row * tmprow = cur.row();
709 int y = cur.y() - tmprow->baseline();
711 setHeightOfRow(bview, tmprow);
713 while (tmprow->previous()
714 && tmprow->previous()->par() == tmprow->par()) {
715 tmprow = tmprow->previous();
716 y -= tmprow->height();
717 setHeightOfRow(bview, tmprow);
720 // we can set the refreshing parameters now
721 status(bview, LyXText::NEED_MORE_REFRESH);
723 refresh_row = tmprow;
724 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
728 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
730 Row * tmprow = cur.row();
732 int y = cur.y() - tmprow->baseline();
733 setHeightOfRow(bview, tmprow);
735 while (tmprow->previous()
736 && tmprow->previous()->par() == tmprow->par()) {
737 tmprow = tmprow->previous();
738 y -= tmprow->height();
741 // we can set the refreshing parameters now
742 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
744 refresh_row = tmprow;
746 status(bview, LyXText::NEED_MORE_REFRESH);
747 setCursor(bview, cur.par(), cur.pos());
751 // deletes and inserts again all paragaphs between the cursor
752 // and the specified par
753 // This function is needed after SetLayout and SetFont etc.
754 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
755 Paragraph const * endpar) const
758 Paragraph * tmppar = 0;
759 Paragraph * first_phys_par = 0;
761 Row * tmprow = cur.row();
763 int y = cur.y() - tmprow->baseline();
765 if (!tmprow->previous()) {
766 // a trick/hack for UNDO
767 // Can somebody please tell me _why_ this solves
769 first_phys_par = firstParagraph();
771 first_phys_par = tmprow->par();
772 while (tmprow->previous()
773 && tmprow->previous()->par() == first_phys_par) {
774 tmprow = tmprow->previous();
775 y -= tmprow->height();
779 // we can set the refreshing parameters now
780 status(bview, LyXText::NEED_MORE_REFRESH);
782 refresh_row = tmprow->previous(); /* the real refresh row will
783 be deleted, so I store
787 tmppar = tmprow->next()->par();
790 while (tmppar != endpar) {
791 removeRow(tmprow->next());
793 tmppar = tmprow->next()->par();
798 // remove the first one
799 tmprow2 = tmprow; /* this is because tmprow->previous()
801 tmprow = tmprow->previous();
804 tmppar = first_phys_par;
808 insertParagraph(bview, tmppar, tmprow);
812 while (tmprow->next()
813 && tmprow->next()->par() == tmppar) {
814 tmprow = tmprow->next();
816 tmppar = tmppar->next();
818 } while (tmppar && tmppar != endpar);
820 // this is because of layout changes
822 refresh_y -= refresh_row->height();
823 setHeightOfRow(bview, refresh_row);
825 refresh_row = firstrow;
827 setHeightOfRow(bview, refresh_row);
830 if (tmprow && tmprow->next())
831 setHeightOfRow(bview, tmprow->next());
835 bool LyXText::fullRebreak(BufferView * bview)
841 if (need_break_row) {
842 breakAgain(bview, need_break_row);
850 // important for the screen
853 /* the cursor set functions have a special mechanism. When they
854 * realize, that you left an empty paragraph, they will delete it.
855 * They also delete the corresponding row */
857 // need the selection cursor:
858 void LyXText::setSelection(BufferView * bview)
860 bool const lsel = selection.set();
862 if (!selection.set()) {
863 last_sel_cursor = selection.cursor;
864 selection.start = selection.cursor;
865 selection.end = selection.cursor;
870 // first the toggling area
871 if (cursor.y() < last_sel_cursor.y()
872 || (cursor.y() == last_sel_cursor.y()
873 && cursor.x() < last_sel_cursor.x())) {
874 toggle_end_cursor = last_sel_cursor;
875 toggle_cursor = cursor;
877 toggle_end_cursor = cursor;
878 toggle_cursor = last_sel_cursor;
881 last_sel_cursor = cursor;
883 // and now the whole selection
885 if (selection.cursor.par() == cursor.par())
886 if (selection.cursor.pos() < cursor.pos()) {
887 selection.end = cursor;
888 selection.start = selection.cursor;
890 selection.end = selection.cursor;
891 selection.start = cursor;
893 else if (selection.cursor.y() < cursor.y() ||
894 (selection.cursor.y() == cursor.y()
895 && selection.cursor.x() < cursor.x())) {
896 selection.end = cursor;
897 selection.start = selection.cursor;
900 selection.end = selection.cursor;
901 selection.start = cursor;
904 // a selection with no contents is not a selection
905 if (selection.start.par() == selection.end.par() &&
906 selection.start.pos() == selection.end.pos())
907 selection.set(false);
909 if (inset_owner && (selection.set() || lsel))
910 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
914 string const LyXText::selectionAsString(Buffer const * buffer) const
916 if (!selection.set()) return string();
919 // Special handling if the whole selection is within one paragraph
920 if (selection.start.par() == selection.end.par()) {
921 result += selection.start.par()->asString(buffer,
922 selection.start.pos(),
923 selection.end.pos());
927 // The selection spans more than one paragraph
929 // First paragraph in selection
930 result += selection.start.par()->asString(buffer,
931 selection.start.pos(),
932 selection.start.par()->size())
935 // The paragraphs in between (if any)
936 LyXCursor tmpcur(selection.start);
937 tmpcur.par(tmpcur.par()->next());
938 while (tmpcur.par() != selection.end.par()) {
939 result += tmpcur.par()->asString(buffer, 0,
940 tmpcur.par()->size()) +"\n\n";
941 tmpcur.par(tmpcur.par()->next());
944 // Last paragraph in selection
945 result += selection.end.par()->asString(buffer, 0,
946 selection.end.pos());
952 void LyXText::clearSelection(BufferView * /*bview*/) const
954 selection.set(false);
955 selection.mark(false);
956 selection.end = selection.start = cursor;
960 void LyXText::cursorHome(BufferView * bview) const
962 setCursor(bview, cursor.par(), cursor.row()->pos());
966 void LyXText::cursorEnd(BufferView * bview) const
968 if (!cursor.row()->next()
969 || cursor.row()->next()->par() != cursor.row()->par()) {
970 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
972 if (cursor.par()->size() &&
973 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
974 || cursor.par()->isNewline(rowLast(cursor.row())))) {
975 setCursor(bview, cursor.par(), rowLast(cursor.row()));
977 setCursor(bview,cursor.par(),
978 rowLast(cursor.row()) + 1);
984 void LyXText::cursorTop(BufferView * bview) const
986 while (cursor.par()->previous())
987 cursor.par(cursor.par()->previous());
988 setCursor(bview, cursor.par(), 0);
992 void LyXText::cursorBottom(BufferView * bview) const
994 while (cursor.par()->next())
995 cursor.par(cursor.par()->next());
996 setCursor(bview, cursor.par(), cursor.par()->size());
1000 void LyXText::toggleFree(BufferView * bview,
1001 LyXFont const & font, bool toggleall)
1003 // If the mask is completely neutral, tell user
1004 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1005 // Could only happen with user style
1006 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1010 // Try implicit word selection
1011 // If there is a change in the language the implicit word selection
1013 LyXCursor resetCursor = cursor;
1014 bool implicitSelection = (font.language() == ignore_language
1015 && font.number() == LyXFont::IGNORE)
1016 ? selectWordWhenUnderCursor(bview) : false;
1019 setFont(bview, font, toggleall);
1021 // Implicit selections are cleared afterwards
1022 //and cursor is set to the original position.
1023 if (implicitSelection) {
1024 clearSelection(bview);
1025 cursor = resetCursor;
1026 setCursor(bview, cursor.par(), cursor.pos());
1027 selection.cursor = cursor;
1030 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1034 Paragraph::size_type
1035 LyXText::beginningOfMainBody(Buffer const * buf,
1036 Paragraph const * par) const
1038 if (textclasslist.Style(buf->params.textclass,
1039 par->getLayout()).labeltype != LABEL_MANUAL)
1042 return par->beginningOfMainBody();
1046 /* the DTP switches for paragraphs. LyX will store them in the
1047 * first physicla paragraph. When a paragraph is broken, the top settings
1048 * rest, the bottom settings are given to the new one. So I can make shure,
1049 * they do not duplicate themself and you cannnot make dirty things with
1052 void LyXText::setParagraph(BufferView * bview,
1053 bool line_top, bool line_bottom,
1054 bool pagebreak_top, bool pagebreak_bottom,
1055 VSpace const & space_top,
1056 VSpace const & space_bottom,
1058 string labelwidthstring,
1061 LyXCursor tmpcursor = cursor;
1062 if (!selection.set()) {
1063 selection.start = cursor;
1064 selection.end = cursor;
1067 // make sure that the depth behind the selection are restored, too
1068 Paragraph * endpar = selection.end.par()->next();
1069 Paragraph * undoendpar = endpar;
1071 if (endpar && endpar->getDepth()) {
1072 while (endpar && endpar->getDepth()) {
1073 endpar = endpar->next();
1074 undoendpar = endpar;
1078 // because of parindents etc.
1079 endpar = endpar->next();
1082 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1085 Paragraph * tmppar = selection.end.par();
1086 while (tmppar != selection.start.par()->previous()) {
1087 setCursor(bview, tmppar, 0);
1088 status(bview, LyXText::NEED_MORE_REFRESH);
1089 refresh_row = cursor.row();
1090 refresh_y = cursor.y() - cursor.row()->baseline();
1091 cursor.par()->params().lineTop(line_top);
1092 cursor.par()->params().lineBottom(line_bottom);
1093 cursor.par()->params().pagebreakTop(pagebreak_top);
1094 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1095 cursor.par()->params().spaceTop(space_top);
1096 cursor.par()->params().spaceBottom(space_bottom);
1097 // does the layout allow the new alignment?
1098 if (align == LYX_ALIGN_LAYOUT)
1099 align = textclasslist
1100 .Style(bview->buffer()->params.textclass,
1101 cursor.par()->getLayout()).align;
1102 if (align & textclasslist
1103 .Style(bview->buffer()->params.textclass,
1104 cursor.par()->getLayout()).alignpossible) {
1105 if (align == textclasslist
1106 .Style(bview->buffer()->params.textclass,
1107 cursor.par()->getLayout()).align)
1108 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1110 cursor.par()->params().align(align);
1112 cursor.par()->setLabelWidthString(labelwidthstring);
1113 cursor.par()->params().noindent(noindent);
1114 tmppar = cursor.par()->previous();
1117 redoParagraphs(bview, selection.start, endpar);
1119 clearSelection(bview);
1120 setCursor(bview, selection.start.par(), selection.start.pos());
1121 selection.cursor = cursor;
1122 setCursor(bview, selection.end.par(), selection.end.pos());
1123 setSelection(bview);
1124 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1126 bview->updateInset(inset_owner, true);
1130 char loweralphaCounter(int n)
1132 if (n < 1 || n > 26)
1142 char alphaCounter(int n)
1144 if (n < 1 || n > 26)
1152 char hebrewCounter(int n)
1154 static const char hebrew[22] = {
1155 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1156 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1159 if (n < 1 || n > 22)
1167 string const romanCounter(int n)
1169 static char const * roman[20] = {
1170 "i", "ii", "iii", "iv", "v",
1171 "vi", "vii", "viii", "ix", "x",
1172 "xi", "xii", "xiii", "xiv", "xv",
1173 "xvi", "xvii", "xviii", "xix", "xx"
1175 if (n < 1 || n > 20)
1184 // set the counter of a paragraph. This includes the labels
1185 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1187 LyXLayout const & layout =
1188 textclasslist.Style(buf->params.textclass,
1191 LyXTextClass const & textclass =
1192 textclasslist.TextClass(buf->params.textclass);
1194 // copy the prev-counters to this one,
1195 // unless this is the first paragraph
1196 if (par->previous()) {
1197 for (int i = 0; i < 10; ++i) {
1198 par->setCounter(i, par->previous()->getFirstCounter(i));
1200 par->params().appendix(par->previous()->params().appendix());
1201 if (!par->params().appendix() && par->params().startOfAppendix()) {
1202 par->params().appendix(true);
1203 for (int i = 0; i < 10; ++i) {
1204 par->setCounter(i, 0);
1207 par->enumdepth = par->previous()->enumdepth;
1208 par->itemdepth = par->previous()->itemdepth;
1210 for (int i = 0; i < 10; ++i) {
1211 par->setCounter(i, 0);
1213 par->params().appendix(par->params().startOfAppendix());
1218 /* Maybe we have to increment the enumeration depth.
1219 * BUT, enumeration in a footnote is considered in isolation from its
1220 * surrounding paragraph so don't increment if this is the
1221 * first line of the footnote
1222 * AND, bibliographies can't have their depth changed ie. they
1223 * are always of depth 0
1226 && par->previous()->getDepth() < par->getDepth()
1227 && textclasslist.Style(buf->params.textclass,
1228 par->previous()->getLayout()
1229 ).labeltype == LABEL_COUNTER_ENUMI
1230 && par->enumdepth < 3
1231 && layout.labeltype != LABEL_BIBLIO) {
1235 // Maybe we have to decrement the enumeration depth, see note above
1237 && par->previous()->getDepth() > par->getDepth()
1238 && layout.labeltype != LABEL_BIBLIO) {
1239 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1240 par->setCounter(6 + par->enumdepth,
1241 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1242 /* reset the counters.
1243 * A depth change is like a breaking layout
1245 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1246 par->setCounter(i, 0);
1249 if (!par->params().labelString().empty()) {
1250 par->params().labelString(string());
1253 if (layout.margintype == MARGIN_MANUAL) {
1254 if (par->params().labelWidthString().empty()) {
1255 par->setLabelWidthString(layout.labelstring());
1258 par->setLabelWidthString(string());
1261 // is it a layout that has an automatic label?
1262 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1264 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1265 if (i >= 0 && i<= buf->params.secnumdepth) {
1266 par->incCounter(i); // increment the counter
1268 // Is there a label? Useful for Chapter layout
1269 if (!par->params().appendix()) {
1270 if (!layout.labelstring().empty())
1271 par->params().labelString(layout.labelstring());
1273 par->params().labelString(string());
1275 if (!layout.labelstring_appendix().empty())
1276 par->params().labelString(layout.labelstring_appendix());
1278 par->params().labelString(string());
1283 if (!par->params().appendix()) {
1284 switch (2 * LABEL_COUNTER_CHAPTER -
1285 textclass.maxcounter() + i) {
1286 case LABEL_COUNTER_CHAPTER:
1287 s << par->getCounter(i);
1289 case LABEL_COUNTER_SECTION:
1290 s << par->getCounter(i - 1) << '.'
1291 << par->getCounter(i);
1293 case LABEL_COUNTER_SUBSECTION:
1294 s << par->getCounter(i - 2) << '.'
1295 << par->getCounter(i - 1) << '.'
1296 << par->getCounter(i);
1298 case LABEL_COUNTER_SUBSUBSECTION:
1299 s << par->getCounter(i - 3) << '.'
1300 << par->getCounter(i - 2) << '.'
1301 << par->getCounter(i - 1) << '.'
1302 << par->getCounter(i);
1305 case LABEL_COUNTER_PARAGRAPH:
1306 s << par->getCounter(i - 4) << '.'
1307 << par->getCounter(i - 3) << '.'
1308 << par->getCounter(i - 2) << '.'
1309 << par->getCounter(i - 1) << '.'
1310 << par->getCounter(i);
1312 case LABEL_COUNTER_SUBPARAGRAPH:
1313 s << par->getCounter(i - 5) << '.'
1314 << par->getCounter(i - 4) << '.'
1315 << par->getCounter(i - 3) << '.'
1316 << par->getCounter(i - 2) << '.'
1317 << par->getCounter(i - 1) << '.'
1318 << par->getCounter(i);
1322 // Can this ever be reached? And in the
1323 // case it is, how can this be correct?
1325 s << par->getCounter(i) << '.';
1328 } else { // appendix
1329 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1330 case LABEL_COUNTER_CHAPTER:
1331 if (par->isRightToLeftPar(buf->params))
1332 s << hebrewCounter(par->getCounter(i));
1334 s << alphaCounter(par->getCounter(i));
1336 case LABEL_COUNTER_SECTION:
1337 if (par->isRightToLeftPar(buf->params))
1338 s << hebrewCounter(par->getCounter(i - 1));
1340 s << alphaCounter(par->getCounter(i - 1));
1343 << par->getCounter(i);
1346 case LABEL_COUNTER_SUBSECTION:
1347 if (par->isRightToLeftPar(buf->params))
1348 s << hebrewCounter(par->getCounter(i - 2));
1350 s << alphaCounter(par->getCounter(i - 2));
1353 << par->getCounter(i-1) << '.'
1354 << par->getCounter(i);
1357 case LABEL_COUNTER_SUBSUBSECTION:
1358 if (par->isRightToLeftPar(buf->params))
1359 s << hebrewCounter(par->getCounter(i-3));
1361 s << alphaCounter(par->getCounter(i-3));
1364 << par->getCounter(i-2) << '.'
1365 << par->getCounter(i-1) << '.'
1366 << par->getCounter(i);
1369 case LABEL_COUNTER_PARAGRAPH:
1370 if (par->isRightToLeftPar(buf->params))
1371 s << hebrewCounter(par->getCounter(i-4));
1373 s << alphaCounter(par->getCounter(i-4));
1376 << par->getCounter(i-3) << '.'
1377 << par->getCounter(i-2) << '.'
1378 << par->getCounter(i-1) << '.'
1379 << par->getCounter(i);
1382 case LABEL_COUNTER_SUBPARAGRAPH:
1383 if (par->isRightToLeftPar(buf->params))
1384 s << hebrewCounter(par->getCounter(i-5));
1386 s << alphaCounter(par->getCounter(i-5));
1389 << par->getCounter(i-4) << '.'
1390 << par->getCounter(i-3) << '.'
1391 << par->getCounter(i-2) << '.'
1392 << par->getCounter(i-1) << '.'
1393 << par->getCounter(i);
1397 // Can this ever be reached? And in the
1398 // case it is, how can this be correct?
1400 s << par->getCounter(i) << '.';
1406 par->params().labelString(par->params().labelString() +s.str().c_str());
1407 // We really want to remove the c_str as soon as
1410 for (i++; i < 10; ++i) {
1411 // reset the following counters
1412 par->setCounter(i, 0);
1414 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1415 for (i++; i < 10; ++i) {
1416 // reset the following counters
1417 par->setCounter(i, 0);
1419 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1420 par->incCounter(i + par->enumdepth);
1421 int number = par->getCounter(i + par->enumdepth);
1425 switch (par->enumdepth) {
1427 if (par->isRightToLeftPar(buf->params))
1429 << hebrewCounter(number)
1433 << loweralphaCounter(number)
1437 if (par->isRightToLeftPar(buf->params))
1438 s << '.' << romanCounter(number);
1440 s << romanCounter(number) << '.';
1443 if (par->isRightToLeftPar(buf->params))
1445 << alphaCounter(number);
1447 s << alphaCounter(number)
1451 if (par->isRightToLeftPar(buf->params))
1458 par->params().labelString(s.str().c_str());
1460 for (i += par->enumdepth + 1; i < 10; ++i) {
1461 // reset the following counters
1462 par->setCounter(i, 0);
1466 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1467 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1469 int number = par->getCounter(i);
1471 InsetCommandParams p( "bibitem" );
1472 par->bibkey = new InsetBibKey(p);
1474 par->bibkey->setCounter(number);
1475 par->params().labelString(layout.labelstring());
1477 // In biblio should't be following counters but...
1479 string s = layout.labelstring();
1481 // the caption hack:
1482 if (layout.labeltype == LABEL_SENSITIVE) {
1483 bool isOK (par->inInset() && par->inInset()->owner() &&
1484 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1487 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1489 = floatList.getType(tmp->type());
1490 // We should get the correct number here too.
1491 s = fl.name() + " #:";
1493 /* par->SetLayout(0);
1494 s = layout->labelstring; */
1495 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1496 ? " :úåòîùî øñç" : "Senseless: ";
1499 par->params().labelString(s);
1501 /* reset the enumeration counter. They are always resetted
1502 * when there is any other layout between */
1503 for (int i = 6 + par->enumdepth; i < 10; ++i)
1504 par->setCounter(i, 0);
1509 // Updates all counters BEHIND the row. Changed paragraphs
1510 // with a dynamic left margin will be rebroken.
1511 void LyXText::updateCounters(BufferView * bview, Row * row) const
1519 par = row->par()->next();
1523 while (row->par() != par)
1526 setCounter(bview->buffer(), par);
1528 // now check for the headline layouts. remember that they
1529 // have a dynamic left margin
1530 if ((textclasslist.Style(bview->buffer()->params.textclass,
1531 par->layout).margintype == MARGIN_DYNAMIC
1532 || textclasslist.Style(bview->buffer()->params.textclass,
1533 par->layout).labeltype == LABEL_SENSITIVE)) {
1535 // Rebreak the paragraph
1536 removeParagraph(row);
1537 appendParagraph(bview, row);
1544 void LyXText::insertInset(BufferView * bview, Inset * inset)
1546 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1548 setUndo(bview, Undo::INSERT,
1549 cursor.par(), cursor.par()->next());
1550 cursor.par()->insertInset(cursor.pos(), inset);
1551 // Just to rebreak and refresh correctly.
1552 // The character will not be inserted a second time
1553 insertChar(bview, Paragraph::META_INSET);
1555 // If we enter a highly editable inset the cursor should be to before
1556 // the inset. This couldn't happen before as Undo was not handled inside
1557 // inset now after the Undo LyX tries to call inset->Edit(...) again
1558 // and cannot do this as the cursor is behind the inset and GetInset
1559 // does not return the inset!
1560 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1561 cursorLeft(bview, true);
1567 void LyXText::copyEnvironmentType()
1569 copylayouttype = cursor.par()->getLayout();
1573 void LyXText::pasteEnvironmentType(BufferView * bview)
1575 setLayout(bview, copylayouttype);
1579 void LyXText::cutSelection(BufferView * bview, bool doclear)
1581 // Stuff what we got on the clipboard. Even if there is no selection.
1583 // There is a problem with having the stuffing here in that the
1584 // larger the selection the slower LyX will get. This can be
1585 // solved by running the line below only when the selection has
1586 // finished. The solution used currently just works, to make it
1587 // faster we need to be more clever and probably also have more
1588 // calls to stuffClipboard. (Lgb)
1589 bview->stuffClipboard(selectionAsString(bview->buffer()));
1591 // This doesn't make sense, if there is no selection
1592 if (!selection.set())
1595 // OK, we have a selection. This is always between selection.start
1596 // and selection.end
1598 // make sure that the depth behind the selection are restored, too
1599 Paragraph * endpar = selection.end.par()->next();
1600 Paragraph * undoendpar = endpar;
1602 if (endpar && endpar->getDepth()) {
1603 while (endpar && endpar->getDepth()) {
1604 endpar = endpar->next();
1605 undoendpar = endpar;
1607 } else if (endpar) {
1608 endpar = endpar->next(); // because of parindents etc.
1611 setUndo(bview, Undo::DELETE,
1612 selection.start.par(), undoendpar);
1614 // there are two cases: cut only within one paragraph or
1615 // more than one paragraph
1616 if (selection.start.par() == selection.end.par()) {
1617 // only within one paragraph
1618 endpar = selection.end.par();
1619 int pos = selection.end.pos();
1620 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1621 selection.start.pos(), pos,
1622 bview->buffer()->params.textclass, doclear);
1623 selection.end.pos(pos);
1625 endpar = selection.end.par();
1626 int pos = selection.end.pos();
1627 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1628 selection.start.pos(), pos,
1629 bview->buffer()->params.textclass, doclear);
1631 selection.end.par(endpar);
1632 selection.end.pos(pos);
1633 cursor.pos(selection.end.pos());
1635 endpar = endpar->next();
1637 // sometimes necessary
1639 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1641 redoParagraphs(bview, selection.start, endpar);
1643 // cutSelection can invalidate the cursor so we need to set
1645 cursor = selection.start;
1647 // need a valid cursor. (Lgb)
1648 clearSelection(bview);
1650 setCursor(bview, cursor.par(), cursor.pos());
1651 selection.cursor = cursor;
1652 updateCounters(bview, cursor.row());
1656 void LyXText::copySelection(BufferView * bview)
1658 // Stuff what we got on the clipboard. Even if there is no selection.
1660 // There is a problem with having the stuffing here in that the
1661 // larger the selection the slower LyX will get. This can be
1662 // solved by running the line below only when the selection has
1663 // finished. The solution used currently just works, to make it
1664 // faster we need to be more clever and probably also have more
1665 // calls to stuffClipboard. (Lgb)
1666 bview->stuffClipboard(selectionAsString(bview->buffer()));
1668 // this doesnt make sense, if there is no selection
1669 if (!selection.set())
1672 // ok we have a selection. This is always between selection.start
1673 // and sel_end cursor
1675 // copy behind a space if there is one
1676 while (selection.start.par()->size() > selection.start.pos()
1677 && selection.start.par()->isLineSeparator(selection.start.pos())
1678 && (selection.start.par() != selection.end.par()
1679 || selection.start.pos() < selection.end.pos()))
1680 selection.start.pos(selection.start.pos() + 1);
1682 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1683 selection.start.pos(), selection.end.pos(),
1684 bview->buffer()->params.textclass);
1688 void LyXText::pasteSelection(BufferView * bview)
1690 // this does not make sense, if there is nothing to paste
1691 if (!CutAndPaste::checkPastePossible(cursor.par()))
1694 setUndo(bview, Undo::INSERT,
1695 cursor.par(), cursor.par()->next());
1698 Paragraph * actpar = cursor.par();
1700 int pos = cursor.pos();
1701 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1702 bview->buffer()->params.textclass);
1704 redoParagraphs(bview, cursor, endpar);
1706 setCursor(bview, cursor.par(), cursor.pos());
1707 clearSelection(bview);
1709 selection.cursor = cursor;
1710 setCursor(bview, actpar, pos);
1711 setSelection(bview);
1712 updateCounters(bview, cursor.row());
1716 // returns a pointer to the very first Paragraph
1717 Paragraph * LyXText::firstParagraph() const
1719 return ownerParagraph();
1723 // sets the selection over the number of characters of string, no check!!
1724 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1729 selection.cursor = cursor;
1730 for (string::size_type i = 0; i < str.length(); ++i)
1732 setSelection(bview);
1736 // simple replacing. The font of the first selected character is used
1737 void LyXText::replaceSelectionWithString(BufferView * bview,
1740 setCursorParUndo(bview);
1743 if (!selection.set()) { // create a dummy selection
1744 selection.end = cursor;
1745 selection.start = cursor;
1748 // Get font setting before we cut
1749 Paragraph::size_type pos = selection.end.pos();
1750 LyXFont const font = selection.start.par()
1751 ->getFontSettings(bview->buffer()->params,
1752 selection.start.pos());
1754 // Insert the new string
1755 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1756 selection.end.par()->insertChar(pos, (*cit), font);
1760 // Cut the selection
1761 cutSelection(bview);
1767 // needed to insert the selection
1768 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1770 Paragraph * par = cursor.par();
1771 Paragraph::size_type pos = cursor.pos();
1772 Paragraph * endpar = cursor.par()->next();
1774 setCursorParUndo(bview);
1776 bool isEnvironment =
1777 textclasslist.Style(bview->buffer()->params.textclass,
1778 cursor.par()->getLayout()).isEnvironment();
1780 textclasslist.Style(bview->buffer()->params.textclass,
1781 cursor.par()->getLayout()).free_spacing;
1783 textclasslist.Style(bview->buffer()->params.textclass,
1784 cursor.par()->getLayout()).keepempty;
1786 // only to be sure, should not be neccessary
1787 clearSelection(bview);
1789 // insert the string, don't insert doublespace
1790 bool space_inserted = true;
1791 for(string::const_iterator cit = str.begin();
1792 cit != str.end(); ++cit) {
1794 if (par->size() || keepempty) {
1795 par->breakParagraph(bview->buffer()->params,
1796 pos, isEnvironment);
1799 space_inserted = true;
1803 // do not insert consecutive spaces if !free_spacing
1804 } else if ((*cit == ' ' || *cit == '\t')
1805 && space_inserted && !free_spacing) {
1807 } else if (*cit == '\t') {
1808 if (!free_spacing) {
1809 // tabs are like spaces here
1810 par->insertChar(pos, ' ',
1813 space_inserted = true;
1815 const Paragraph::value_type nb = 8 - pos % 8;
1816 for (Paragraph::size_type a = 0;
1818 par->insertChar(pos, ' ',
1822 space_inserted = true;
1824 } else if (!IsPrintable(*cit)) {
1825 // Ignore unprintables
1828 // just insert the character
1829 par->insertChar(pos, *cit, current_font);
1831 space_inserted = (*cit == ' ');
1836 redoParagraphs(bview, cursor, endpar);
1837 setCursor(bview, cursor.par(), cursor.pos());
1838 selection.cursor = cursor;
1839 setCursor(bview, par, pos);
1840 setSelection(bview);
1844 // turns double-CR to single CR, others where converted into one
1845 // blank. Then InsertStringAsLines is called
1846 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1848 string linestr(str);
1849 bool newline_inserted = false;
1850 for (string::size_type i = 0; i < linestr.length(); ++i) {
1851 if (linestr[i] == '\n') {
1852 if (newline_inserted) {
1853 // we know that \r will be ignored by
1854 // InsertStringA. Of course, it is a dirty
1855 // trick, but it works...
1856 linestr[i - 1] = '\r';
1860 newline_inserted = true;
1862 } else if (IsPrintable(linestr[i])) {
1863 newline_inserted = false;
1866 insertStringAsLines(bview, linestr);
1870 bool LyXText::gotoNextInset(BufferView * bview,
1871 std::vector<Inset::Code> const & codes,
1872 string const & contents) const
1874 LyXCursor res = cursor;
1877 if (res.pos() < res.par()->size() - 1) {
1878 res.pos(res.pos() + 1);
1880 res.par(res.par()->next());
1884 } while (res.par() &&
1885 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1886 && (inset = res.par()->getInset(res.pos())) != 0
1887 && find(codes.begin(), codes.end(), inset->lyxCode())
1889 && (contents.empty() ||
1890 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1894 setCursor(bview, res.par(), res.pos());
1901 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1902 Paragraph::size_type pos)
1904 LyXCursor tmpcursor;
1907 Paragraph::size_type z;
1908 Row * row = getRow(par, pos, y);
1910 // is there a break one row above
1911 if (row->previous() && row->previous()->par() == row->par()) {
1912 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1913 if (z >= row->pos()) {
1914 // set the dimensions of the row above
1915 y -= row->previous()->height();
1917 refresh_row = row->previous();
1918 status(bview, LyXText::NEED_MORE_REFRESH);
1920 breakAgain(bview, row->previous());
1922 // set the cursor again. Otherwise
1923 // dangling pointers are possible
1924 setCursor(bview, cursor.par(), cursor.pos(),
1925 false, cursor.boundary());
1926 selection.cursor = cursor;
1931 int const tmpheight = row->height();
1932 Paragraph::size_type const tmplast = rowLast(row);
1936 breakAgain(bview, row);
1937 if (row->height() == tmpheight && rowLast(row) == tmplast)
1938 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1940 status(bview, LyXText::NEED_MORE_REFRESH);
1942 // check the special right address boxes
1943 if (textclasslist.Style(bview->buffer()->params.textclass,
1944 par->getLayout()).margintype
1945 == MARGIN_RIGHT_ADDRESS_BOX) {
1952 redoDrawingOfParagraph(bview, tmpcursor);
1955 // set the cursor again. Otherwise dangling pointers are possible
1956 // also set the selection
1958 if (selection.set()) {
1960 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1961 false, selection.cursor.boundary());
1962 selection.cursor = cursor;
1963 setCursorIntern(bview, selection.start.par(),
1964 selection.start.pos(),
1965 false, selection.start.boundary());
1966 selection.start = cursor;
1967 setCursorIntern(bview, selection.end.par(),
1968 selection.end.pos(),
1969 false, selection.end.boundary());
1970 selection.end = cursor;
1971 setCursorIntern(bview, last_sel_cursor.par(),
1972 last_sel_cursor.pos(),
1973 false, last_sel_cursor.boundary());
1974 last_sel_cursor = cursor;
1977 setCursorIntern(bview, cursor.par(), cursor.pos(),
1978 false, cursor.boundary());
1982 // returns false if inset wasn't found
1983 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1985 // first check the current paragraph
1986 int pos = cursor.par()->getPositionOfInset(inset);
1988 checkParagraph(bview, cursor.par(), pos);
1992 // check every paragraph
1994 Paragraph * par = firstParagraph();
1996 pos = par->getPositionOfInset(inset);
1998 checkParagraph(bview, par, pos);
2008 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2009 Paragraph::size_type pos,
2010 bool setfont, bool boundary) const
2012 LyXCursor old_cursor = cursor;
2013 setCursorIntern(bview, par, pos, setfont, boundary);
2014 deleteEmptyParagraphMechanism(bview, old_cursor);
2018 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2019 Paragraph::size_type pos, bool boundary) const
2023 cur.boundary(boundary);
2025 // get the cursor y position in text
2027 Row * row = getRow(par, pos, y);
2028 // y is now the beginning of the cursor row
2029 y += row->baseline();
2030 // y is now the cursor baseline
2033 // now get the cursors x position
2035 float fill_separator;
2037 float fill_label_hfill;
2038 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2040 Paragraph::size_type cursor_vpos = 0;
2041 Paragraph::size_type last = rowLastPrintable(row);
2043 if (pos > last + 1) // This shouldn't happen.
2045 else if (pos < row->pos())
2048 if (last < row->pos())
2049 cursor_vpos = row->pos();
2050 else if (pos > last && !boundary)
2051 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2052 ? row->pos() : last + 1;
2053 else if (pos > row->pos() &&
2054 (pos > last || boundary))
2055 /// Place cursor after char at (logical) position pos - 1
2056 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2057 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2059 /// Place cursor before char at (logical) position pos
2060 cursor_vpos = (bidi_level(pos) % 2 == 0)
2061 ? log2vis(pos) : log2vis(pos) + 1;
2063 Paragraph::size_type main_body =
2064 beginningOfMainBody(bview->buffer(), row->par());
2065 if ((main_body > 0) &&
2066 ((main_body-1 > last) ||
2067 !row->par()->isLineSeparator(main_body-1)))
2070 for (Paragraph::size_type vpos = row->pos();
2071 vpos < cursor_vpos; ++vpos) {
2072 pos = vis2log(vpos);
2073 if (main_body > 0 && pos == main_body - 1) {
2074 x += fill_label_hfill +
2075 lyxfont::width(textclasslist.Style(
2076 bview->buffer()->params.textclass,
2077 row->par()->getLayout())
2079 getFont(bview->buffer(), row->par(), -2));
2080 if (row->par()->isLineSeparator(main_body-1))
2081 x -= singleWidth(bview, row->par(),main_body-1);
2083 if (hfillExpansion(bview->buffer(), row, pos)) {
2084 x += singleWidth(bview, row->par(), pos);
2085 if (pos >= main_body)
2088 x += fill_label_hfill;
2089 } else if (row->par()->isSeparator(pos)) {
2090 x += singleWidth(bview, row->par(), pos);
2091 if (pos >= main_body)
2092 x += fill_separator;
2094 x += singleWidth(bview, row->par(), pos);
2103 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2104 Paragraph::size_type pos,
2105 bool setfont, bool boundary) const
2107 InsetText * it = static_cast<InsetText *>(par->inInset());
2108 if (it && (it != inset_owner)) {
2109 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2112 setCursor(bview, cursor, par, pos, boundary);
2114 setCurrentFont(bview);
2119 void LyXText::setCurrentFont(BufferView * bview) const
2121 Paragraph::size_type pos = cursor.pos();
2122 if (cursor.boundary() && pos > 0)
2126 if (pos == cursor.par()->size())
2128 else // potentional bug... BUG (Lgb)
2129 if (cursor.par()->isSeparator(pos)) {
2130 if (pos > cursor.row()->pos() &&
2131 bidi_level(pos) % 2 ==
2132 bidi_level(pos - 1) % 2)
2134 else if (pos + 1 < cursor.par()->size())
2140 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2141 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2143 if (cursor.pos() == cursor.par()->size() &&
2144 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2145 !cursor.boundary()) {
2146 Language const * lang =
2147 cursor.par()->getParLanguage(bview->buffer()->params);
2148 current_font.setLanguage(lang);
2149 current_font.setNumber(LyXFont::OFF);
2150 real_current_font.setLanguage(lang);
2151 real_current_font.setNumber(LyXFont::OFF);
2156 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2158 LyXCursor old_cursor = cursor;
2160 // Get the row first.
2162 Row * row = getRowNearY(y);
2163 cursor.par(row->par());
2166 int column = getColumnNearX(bview, row, x, bound);
2167 cursor.pos(row->pos() + column);
2169 cursor.y(y + row->baseline());
2171 cursor.boundary(bound);
2172 setCurrentFont(bview);
2173 deleteEmptyParagraphMechanism(bview, old_cursor);
2177 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2180 // Get the row first.
2182 Row * row = getRowNearY(y);
2184 int column = getColumnNearX(bview, row, x, bound);
2186 cur.par(row->par());
2187 cur.pos(row->pos() + column);
2189 cur.y(y + row->baseline());
2191 cur.boundary(bound);
2195 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2197 if (cursor.pos() > 0) {
2198 bool boundary = cursor.boundary();
2199 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2200 if (!internal && !boundary &&
2201 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2202 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2203 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2204 Paragraph * par = cursor.par()->previous();
2205 setCursor(bview, par, par->size());
2210 void LyXText::cursorRight(BufferView * bview, bool internal) const
2212 if (!internal && cursor.boundary() &&
2213 !cursor.par()->isNewline(cursor.pos()))
2214 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2215 else if (cursor.pos() < cursor.par()->size()) {
2216 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2218 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2219 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2220 } else if (cursor.par()->next())
2221 setCursor(bview, cursor.par()->next(), 0);
2225 void LyXText::cursorUp(BufferView * bview) const
2227 setCursorFromCoordinates(bview, cursor.x_fix(),
2228 cursor.y() - cursor.row()->baseline() - 1);
2232 void LyXText::cursorDown(BufferView * bview) const
2234 setCursorFromCoordinates(bview, cursor.x_fix(),
2235 cursor.y() - cursor.row()->baseline()
2236 + cursor.row()->height() + 1);
2240 void LyXText::cursorUpParagraph(BufferView * bview) const
2242 if (cursor.pos() > 0) {
2243 setCursor(bview, cursor.par(), 0);
2245 else if (cursor.par()->previous()) {
2246 setCursor(bview, cursor.par()->previous(), 0);
2251 void LyXText::cursorDownParagraph(BufferView * bview) const
2253 if (cursor.par()->next()) {
2254 setCursor(bview, cursor.par()->next(), 0);
2256 setCursor(bview, cursor.par(), cursor.par()->size());
2261 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2262 LyXCursor const & old_cursor) const
2264 // Would be wrong to delete anything if we have a selection.
2265 if (selection.set()) return;
2267 // We allow all kinds of "mumbo-jumbo" when freespacing.
2268 if (textclasslist.Style(bview->buffer()->params.textclass,
2269 old_cursor.par()->getLayout()).free_spacing)
2272 bool deleted = false;
2274 /* Ok I'll put some comments here about what is missing.
2275 I have fixed BackSpace (and thus Delete) to not delete
2276 double-spaces automagically. I have also changed Cut,
2277 Copy and Paste to hopefully do some sensible things.
2278 There are still some small problems that can lead to
2279 double spaces stored in the document file or space at
2280 the beginning of paragraphs. This happens if you have
2281 the cursor betwenn to spaces and then save. Or if you
2282 cut and paste and the selection have a space at the
2283 beginning and then save right after the paste. I am
2284 sure none of these are very hard to fix, but I will
2285 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2286 that I can get some feedback. (Lgb)
2289 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2290 // delete the LineSeparator.
2293 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2294 // delete the LineSeparator.
2297 // If the pos around the old_cursor were spaces, delete one of them.
2298 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2299 // Only if the cursor has really moved
2301 if (old_cursor.pos() > 0
2302 && old_cursor.pos() < old_cursor.par()->size()
2303 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2304 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2305 old_cursor.par()->erase(old_cursor.pos() - 1);
2306 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2308 if (old_cursor.par() == cursor.par() &&
2309 cursor.pos() > old_cursor.pos()) {
2310 setCursorIntern(bview, cursor.par(),
2313 setCursorIntern(bview, cursor.par(),
2319 // Do not delete empty paragraphs with keepempty set.
2320 if ((textclasslist.Style(bview->buffer()->params.textclass,
2321 old_cursor.par()->getLayout())).keepempty)
2324 LyXCursor tmpcursor;
2326 if (old_cursor.par() != cursor.par()) {
2327 if ((old_cursor.par()->size() == 0
2328 || (old_cursor.par()->size() == 1
2329 && old_cursor.par()->isLineSeparator(0)))) {
2330 // ok, we will delete anything
2332 // make sure that you do not delete any environments
2333 status(bview, LyXText::NEED_MORE_REFRESH);
2336 if (old_cursor.row()->previous()) {
2337 refresh_row = old_cursor.row()->previous();
2338 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2340 cursor = old_cursor; // that undo can restore the right cursor position
2341 Paragraph * endpar = old_cursor.par()->next();
2342 if (endpar && endpar->getDepth()) {
2343 while (endpar && endpar->getDepth()) {
2344 endpar = endpar->next();
2347 setUndo(bview, Undo::DELETE,
2353 removeRow(old_cursor.row());
2354 if (ownerParagraph() == old_cursor.par()) {
2355 ownerParagraph(ownerParagraph()->next());
2358 delete old_cursor.par();
2360 /* Breakagain the next par. Needed
2361 * because of the parindent that
2362 * can occur or dissappear. The
2363 * next row can change its height,
2364 * if there is another layout before */
2365 if (refresh_row->next()) {
2366 breakAgain(bview, refresh_row->next());
2367 updateCounters(bview, refresh_row);
2369 setHeightOfRow(bview, refresh_row);
2371 refresh_row = old_cursor.row()->next();
2372 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2375 cursor = old_cursor; // that undo can restore the right cursor position
2376 Paragraph * endpar = old_cursor.par()->next();
2377 if (endpar && endpar->getDepth()) {
2378 while (endpar && endpar->getDepth()) {
2379 endpar = endpar->next();
2382 setUndo(bview, Undo::DELETE,
2388 removeRow(old_cursor.row());
2390 if (ownerParagraph() == old_cursor.par()) {
2391 ownerParagraph(ownerParagraph()->next());
2394 delete old_cursor.par();
2396 /* Breakagain the next par. Needed
2397 because of the parindent that can
2398 occur or dissappear.
2399 The next row can change its height,
2400 if there is another layout before
2403 breakAgain(bview, refresh_row);
2404 updateCounters(bview, refresh_row->previous());
2410 setCursorIntern(bview, cursor.par(), cursor.pos());
2412 if (selection.cursor.par() == old_cursor.par()
2413 && selection.cursor.pos() == selection.cursor.pos()) {
2414 // correct selection
2415 selection.cursor = cursor;
2419 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2420 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2422 setCursorIntern(bview, cursor.par(), cursor.pos());
2423 selection.cursor = cursor;
2430 void LyXText::toggleAppendix(BufferView * bview)
2432 Paragraph * par = cursor.par();
2433 bool start = !par->params().startOfAppendix();
2435 // ensure that we have only one start_of_appendix in this document
2436 Paragraph * tmp = firstParagraph();
2437 for (; tmp; tmp = tmp->next()) {
2438 tmp->params().startOfAppendix(false);
2441 par->params().startOfAppendix(start);
2443 // we can set the refreshing parameters now
2444 status(bview, LyXText::NEED_MORE_REFRESH);
2446 refresh_row = 0; // not needed for full update
2447 updateCounters(bview, 0);
2448 setCursor(bview, cursor.par(), cursor.pos());
2452 Paragraph * LyXText::ownerParagraph() const
2455 return inset_owner->paragraph();
2457 return bv_owner->buffer()->paragraph;
2461 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2464 inset_owner->paragraph(p);
2466 bv_owner->buffer()->paragraph = p;
2471 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2473 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2474 if (op && op->inInset()) {
2475 static_cast<InsetText *>(op->inInset())->paragraph(p);
2478 inset_owner->paragraph(p);
2480 bv_owner->buffer()->paragraph = p;
2487 LyXText::text_status LyXText::status() const
2493 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2496 if ((status_ != NEED_MORE_REFRESH)
2497 || (status_ == NEED_MORE_REFRESH)
2498 && (st != NEED_VERY_LITTLE_REFRESH)) {
2500 if (inset_owner && st != UNCHANGED) {
2501 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2505 #warning Please tell what the intention is here. (Lgb)
2506 // The above does not make any sense, I changed it to what is here,
2507 // but it still does not make much sense. (Lgb)
2508 #warning Sure have a look now! (Jug)
2509 // well as much as I know && binds more then || so the above and the
2510 // below are identical (this for your known use of parentesis!)
2511 // Now some explanation:
2512 // We should only go up with refreshing code so this means that if
2513 // we have a MORE refresh we should never set it to LITTLE if we still
2514 // didn't handle it (and then it will be UNCHANGED. Now as long as
2515 // we stay inside one LyXText this may work but we need to tell the
2516 // outermost LyXText that it should REALLY draw us if there is some
2517 // change in a Inset::LyXText. So you see that when we are inside a
2518 // inset's LyXText we give the LITTLE to the outermost LyXText to
2519 // tell'em that it should redraw the actual row (where the inset
2520 // resides! Capito?!
2522 if ((status_ != NEED_MORE_REFRESH)
2523 || (status_ == NEED_MORE_REFRESH
2524 && st != NEED_VERY_LITTLE_REFRESH))
2527 if (inset_owner && st != UNCHANGED) {
2528 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);