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 void LyXText::openStuff(BufferView * bview)
365 if (cursor.pos() == 0 && cursor.par()->bibkey){
366 cursor.par()->bibkey->edit(bview, 0, 0, 0);
367 } else if (cursor.pos() < cursor.par()->size()
368 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
369 Inset * inset = cursor.par()->getInset(cursor.pos());
370 if (!inset->editable())
372 bview->owner()->message(inset->editMessage());
373 if (inset->editable() != Inset::HIGHLY_EDITABLE)
374 setCursorParUndo(bview);
375 inset->edit(bview, 0, 0, 0);
380 /* used in setlayout */
381 // Asger is not sure we want to do this...
382 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
385 LyXLayout const & layout =
386 textclasslist.Style(buf->params.textclass, par->getLayout());
388 LyXFont layoutfont, tmpfont;
389 for (Paragraph::size_type pos = 0;
390 pos < par->size(); ++pos) {
391 if (pos < beginningOfMainBody(buf, par))
392 layoutfont = layout.labelfont;
394 layoutfont = layout.font;
396 tmpfont = par->getFontSettings(buf->params, pos);
397 tmpfont.reduce(layoutfont);
398 par->setFont(pos, tmpfont);
403 Paragraph * LyXText::setLayout(BufferView * bview,
404 LyXCursor & cur, LyXCursor & sstart_cur,
405 LyXCursor & send_cur,
406 LyXTextClass::size_type layout)
408 Paragraph * endpar = send_cur.par()->next();
409 Paragraph * undoendpar = endpar;
411 if (endpar && endpar->getDepth()) {
412 while (endpar && endpar->getDepth()) {
413 endpar = endpar->next();
417 endpar = endpar->next(); // because of parindents etc.
420 setUndo(bview, Undo::EDIT,
421 sstart_cur.par(), undoendpar);
423 // ok we have a selection. This is always between sstart_cur
424 // and sel_end cursor
427 LyXLayout const & lyxlayout =
428 textclasslist.Style(bview->buffer()->params.textclass, layout);
430 while (cur.par() != send_cur.par()) {
431 cur.par()->setLayout(layout);
432 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
433 Paragraph * fppar = cur.par();
434 fppar->params().spaceTop(lyxlayout.fill_top ?
435 VSpace(VSpace::VFILL)
436 : VSpace(VSpace::NONE));
437 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
438 VSpace(VSpace::VFILL)
439 : VSpace(VSpace::NONE));
440 if (lyxlayout.margintype == MARGIN_MANUAL)
441 cur.par()->setLabelWidthString(lyxlayout.labelstring());
442 if (lyxlayout.labeltype != LABEL_BIBLIO
444 delete fppar->bibkey;
447 cur.par(cur.par()->next());
449 cur.par()->setLayout(layout);
450 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
451 Paragraph * fppar = cur.par();
452 fppar->params().spaceTop(lyxlayout.fill_top ?
453 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
454 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
455 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
456 if (lyxlayout.margintype == MARGIN_MANUAL)
457 cur.par()->setLabelWidthString(lyxlayout.labelstring());
458 if (lyxlayout.labeltype != LABEL_BIBLIO
460 delete fppar->bibkey;
467 // set layout over selection and make a total rebreak of those paragraphs
468 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
470 LyXCursor tmpcursor = cursor; /* store the current cursor */
472 // if there is no selection just set the layout
473 // of the current paragraph */
474 if (!selection.set()) {
475 selection.start = cursor; // dummy selection
476 selection.end = cursor;
478 Paragraph * endpar = setLayout(bview, cursor, selection.start,
479 selection.end, layout);
480 redoParagraphs(bview, selection.start, endpar);
482 // we have to reset the selection, because the
483 // geometry could have changed
484 setCursor(bview, selection.start.par(),
485 selection.start.pos(), false);
486 selection.cursor = cursor;
487 setCursor(bview, selection.end.par(), selection.end.pos(), false);
488 updateCounters(bview, cursor.row());
489 clearSelection(bview);
491 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
495 // increment depth over selection and
496 // make a total rebreak of those paragraphs
497 void LyXText::incDepth(BufferView * bview)
499 // If there is no selection, just use the current paragraph
500 if (!selection.set()) {
501 selection.start = cursor; // dummy selection
502 selection.end = cursor;
505 // We end at the next paragraph with depth 0
506 Paragraph * endpar = selection.end.par()->next();
508 Paragraph * undoendpar = endpar;
510 if (endpar && endpar->getDepth()) {
511 while (endpar && endpar->getDepth()) {
512 endpar = endpar->next();
516 endpar = endpar->next(); // because of parindents etc.
519 setUndo(bview, Undo::EDIT,
520 selection.start.par(), undoendpar);
522 LyXCursor tmpcursor = cursor; // store the current cursor
524 // ok we have a selection. This is always between sel_start_cursor
525 // and sel_end cursor
526 cursor = selection.start;
528 bool anything_changed = false;
531 // NOTE: you can't change the depth of a bibliography entry
533 textclasslist.Style(bview->buffer()->params.textclass,
534 cursor.par()->getLayout()
535 ).labeltype != LABEL_BIBLIO) {
536 Paragraph * prev = cursor.par()->previous();
539 && (prev->getDepth() - cursor.par()->getDepth() > 0
540 || (prev->getDepth() == cursor.par()->getDepth()
541 && textclasslist.Style(bview->buffer()->params.textclass,
542 prev->getLayout()).isEnvironment()))) {
543 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
544 anything_changed = true;
547 if (cursor.par() == selection.end.par())
549 cursor.par(cursor.par()->next());
552 // if nothing changed set all depth to 0
553 if (!anything_changed) {
554 cursor = selection.start;
555 while (cursor.par() != selection.end.par()) {
556 cursor.par()->params().depth(0);
557 cursor.par(cursor.par()->next());
559 cursor.par()->params().depth(0);
562 redoParagraphs(bview, selection.start, endpar);
564 // we have to reset the selection, because the
565 // geometry could have changed
566 setCursor(bview, selection.start.par(), selection.start.pos());
567 selection.cursor = cursor;
568 setCursor(bview, selection.end.par(), selection.end.pos());
569 updateCounters(bview, cursor.row());
570 clearSelection(bview);
572 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
576 // decrement depth over selection and
577 // make a total rebreak of those paragraphs
578 void LyXText::decDepth(BufferView * bview)
580 // if there is no selection just set the layout
581 // of the current paragraph
582 if (!selection.set()) {
583 selection.start = cursor; // dummy selection
584 selection.end = cursor;
586 Paragraph * endpar = selection.end.par()->next();
587 Paragraph * undoendpar = endpar;
589 if (endpar && endpar->getDepth()) {
590 while (endpar && endpar->getDepth()) {
591 endpar = endpar->next();
595 endpar = endpar->next(); // because of parindents etc.
598 setUndo(bview, Undo::EDIT,
599 selection.start.par(), undoendpar);
601 LyXCursor tmpcursor = cursor; // store the current cursor
603 // ok we have a selection. This is always between sel_start_cursor
604 // and sel_end cursor
605 cursor = selection.start;
608 if (cursor.par()->params().depth()) {
609 cursor.par()->params()
610 .depth(cursor.par()->params().depth() - 1);
612 if (cursor.par() == selection.end.par()) {
615 cursor.par(cursor.par()->next());
618 redoParagraphs(bview, selection.start, endpar);
620 // we have to reset the selection, because the
621 // geometry could have changed
622 setCursor(bview, selection.start.par(),
623 selection.start.pos());
624 selection.cursor = cursor;
625 setCursor(bview, selection.end.par(), selection.end.pos());
626 updateCounters(bview, cursor.row());
627 clearSelection(bview);
629 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
633 // set font over selection and make a total rebreak of those paragraphs
634 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
636 // if there is no selection just set the current_font
637 if (!selection.set()) {
638 // Determine basis font
640 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
642 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
644 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
645 // Update current font
646 real_current_font.update(font,
647 bview->buffer()->params.language,
650 // Reduce to implicit settings
651 current_font = real_current_font;
652 current_font.reduce(layoutfont);
653 // And resolve it completely
654 real_current_font.realize(layoutfont);
658 LyXCursor tmpcursor = cursor; // store the current cursor
660 // ok we have a selection. This is always between sel_start_cursor
661 // and sel_end cursor
663 setUndo(bview, Undo::EDIT,
664 selection.start.par(),
665 selection.end.par()->next());
667 cursor = selection.start;
668 while (cursor.par() != selection.end.par() ||
669 (cursor.pos() < selection.end.pos())) {
670 if (cursor.pos() < cursor.par()->size()) {
671 // an open footnote should behave
673 setCharFont(bview, cursor.par(), cursor.pos(),
675 cursor.pos(cursor.pos() + 1);
678 cursor.par(cursor.par()->next());
683 redoParagraphs(bview, selection.start, selection.end.par()->next());
685 // we have to reset the selection, because the
686 // geometry could have changed
687 setCursor(bview, selection.start.par(), selection.start.pos());
688 selection.cursor = cursor;
689 setCursor(bview, selection.end.par(), selection.end.pos());
690 clearSelection(bview);
692 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
693 tmpcursor.boundary());
697 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
699 Row * tmprow = cur.row();
700 int y = cur.y() - tmprow->baseline();
702 setHeightOfRow(bview, tmprow);
704 while (tmprow->previous()
705 && tmprow->previous()->par() == tmprow->par()) {
706 tmprow = tmprow->previous();
707 y -= tmprow->height();
708 setHeightOfRow(bview, tmprow);
711 // we can set the refreshing parameters now
712 status(bview, LyXText::NEED_MORE_REFRESH);
714 refresh_row = tmprow;
715 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
719 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
721 Row * tmprow = cur.row();
723 int y = cur.y() - tmprow->baseline();
724 setHeightOfRow(bview, tmprow);
726 while (tmprow->previous()
727 && tmprow->previous()->par() == tmprow->par()) {
728 tmprow = tmprow->previous();
729 y -= tmprow->height();
732 // we can set the refreshing parameters now
733 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
735 refresh_row = tmprow;
737 status(bview, LyXText::NEED_MORE_REFRESH);
738 setCursor(bview, cur.par(), cur.pos());
742 // deletes and inserts again all paragaphs between the cursor
743 // and the specified par
744 // This function is needed after SetLayout and SetFont etc.
745 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
746 Paragraph const * endpar) const
749 Paragraph * tmppar = 0;
750 Paragraph * first_phys_par = 0;
752 Row * tmprow = cur.row();
754 int y = cur.y() - tmprow->baseline();
756 if (!tmprow->previous()) {
757 // a trick/hack for UNDO
758 // Can somebody please tell me _why_ this solves
760 first_phys_par = firstParagraph();
762 first_phys_par = tmprow->par();
763 while (tmprow->previous()
764 && tmprow->previous()->par() == first_phys_par) {
765 tmprow = tmprow->previous();
766 y -= tmprow->height();
770 // we can set the refreshing parameters now
771 status(bview, LyXText::NEED_MORE_REFRESH);
773 refresh_row = tmprow->previous(); /* the real refresh row will
774 be deleted, so I store
778 tmppar = tmprow->next()->par();
781 while (tmppar != endpar) {
782 removeRow(tmprow->next());
784 tmppar = tmprow->next()->par();
789 // remove the first one
790 tmprow2 = tmprow; /* this is because tmprow->previous()
792 tmprow = tmprow->previous();
795 tmppar = first_phys_par;
799 insertParagraph(bview, tmppar, tmprow);
803 while (tmprow->next()
804 && tmprow->next()->par() == tmppar) {
805 tmprow = tmprow->next();
807 tmppar = tmppar->next();
809 } while (tmppar && tmppar != endpar);
811 // this is because of layout changes
813 refresh_y -= refresh_row->height();
814 setHeightOfRow(bview, refresh_row);
816 refresh_row = firstrow;
818 setHeightOfRow(bview, refresh_row);
821 if (tmprow && tmprow->next())
822 setHeightOfRow(bview, tmprow->next());
826 bool LyXText::fullRebreak(BufferView * bview)
832 if (need_break_row) {
833 breakAgain(bview, need_break_row);
841 // important for the screen
844 /* the cursor set functions have a special mechanism. When they
845 * realize, that you left an empty paragraph, they will delete it.
846 * They also delete the corresponding row */
848 // need the selection cursor:
849 void LyXText::setSelection(BufferView * bview)
851 bool const lsel = selection.set();
853 if (!selection.set()) {
854 last_sel_cursor = selection.cursor;
855 selection.start = selection.cursor;
856 selection.end = selection.cursor;
861 // first the toggling area
862 if (cursor.y() < last_sel_cursor.y()
863 || (cursor.y() == last_sel_cursor.y()
864 && cursor.x() < last_sel_cursor.x())) {
865 toggle_end_cursor = last_sel_cursor;
866 toggle_cursor = cursor;
868 toggle_end_cursor = cursor;
869 toggle_cursor = last_sel_cursor;
872 last_sel_cursor = cursor;
874 // and now the whole selection
876 if (selection.cursor.par() == cursor.par())
877 if (selection.cursor.pos() < cursor.pos()) {
878 selection.end = cursor;
879 selection.start = selection.cursor;
881 selection.end = selection.cursor;
882 selection.start = cursor;
884 else if (selection.cursor.y() < cursor.y() ||
885 (selection.cursor.y() == cursor.y()
886 && selection.cursor.x() < cursor.x())) {
887 selection.end = cursor;
888 selection.start = selection.cursor;
891 selection.end = selection.cursor;
892 selection.start = cursor;
895 // a selection with no contents is not a selection
896 if (selection.start.par() == selection.end.par() &&
897 selection.start.pos() == selection.end.pos())
898 selection.set(false);
900 if (inset_owner && (selection.set() || lsel))
901 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
905 string const LyXText::selectionAsString(Buffer const * buffer) const
907 if (!selection.set()) return string();
910 // Special handling if the whole selection is within one paragraph
911 if (selection.start.par() == selection.end.par()) {
912 result += selection.start.par()->asString(buffer,
913 selection.start.pos(),
914 selection.end.pos());
918 // The selection spans more than one paragraph
920 // First paragraph in selection
921 result += selection.start.par()->asString(buffer,
922 selection.start.pos(),
923 selection.start.par()->size())
926 // The paragraphs in between (if any)
927 LyXCursor tmpcur(selection.start);
928 tmpcur.par(tmpcur.par()->next());
929 while (tmpcur.par() != selection.end.par()) {
930 result += tmpcur.par()->asString(buffer, 0,
931 tmpcur.par()->size()) +"\n\n";
932 tmpcur.par(tmpcur.par()->next());
935 // Last paragraph in selection
936 result += selection.end.par()->asString(buffer, 0,
937 selection.end.pos());
943 void LyXText::clearSelection(BufferView * /*bview*/) const
945 selection.set(false);
946 selection.mark(false);
947 selection.end = selection.start = cursor;
951 void LyXText::cursorHome(BufferView * bview) const
953 setCursor(bview, cursor.par(), cursor.row()->pos());
957 void LyXText::cursorEnd(BufferView * bview) const
959 if (!cursor.row()->next()
960 || cursor.row()->next()->par() != cursor.row()->par()) {
961 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
963 if (cursor.par()->size() &&
964 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
965 || cursor.par()->isNewline(rowLast(cursor.row())))) {
966 setCursor(bview, cursor.par(), rowLast(cursor.row()));
968 setCursor(bview,cursor.par(),
969 rowLast(cursor.row()) + 1);
975 void LyXText::cursorTop(BufferView * bview) const
977 while (cursor.par()->previous())
978 cursor.par(cursor.par()->previous());
979 setCursor(bview, cursor.par(), 0);
983 void LyXText::cursorBottom(BufferView * bview) const
985 while (cursor.par()->next())
986 cursor.par(cursor.par()->next());
987 setCursor(bview, cursor.par(), cursor.par()->size());
991 void LyXText::toggleFree(BufferView * bview,
992 LyXFont const & font, bool toggleall)
994 // If the mask is completely neutral, tell user
995 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
996 // Could only happen with user style
997 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1001 // Try implicit word selection
1002 // If there is a change in the language the implicit word selection
1004 LyXCursor resetCursor = cursor;
1005 bool implicitSelection = (font.language() == ignore_language
1006 && font.number() == LyXFont::IGNORE)
1007 ? selectWordWhenUnderCursor(bview) : false;
1010 setFont(bview, font, toggleall);
1012 // Implicit selections are cleared afterwards
1013 //and cursor is set to the original position.
1014 if (implicitSelection) {
1015 clearSelection(bview);
1016 cursor = resetCursor;
1017 setCursor(bview, cursor.par(), cursor.pos());
1018 selection.cursor = cursor;
1021 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1025 Paragraph::size_type
1026 LyXText::beginningOfMainBody(Buffer const * buf,
1027 Paragraph const * par) const
1029 if (textclasslist.Style(buf->params.textclass,
1030 par->getLayout()).labeltype != LABEL_MANUAL)
1033 return par->beginningOfMainBody();
1037 /* the DTP switches for paragraphs. LyX will store them in the
1038 * first physicla paragraph. When a paragraph is broken, the top settings
1039 * rest, the bottom settings are given to the new one. So I can make shure,
1040 * they do not duplicate themself and you cannnot make dirty things with
1043 void LyXText::setParagraph(BufferView * bview,
1044 bool line_top, bool line_bottom,
1045 bool pagebreak_top, bool pagebreak_bottom,
1046 VSpace const & space_top,
1047 VSpace const & space_bottom,
1049 string labelwidthstring,
1052 LyXCursor tmpcursor = cursor;
1053 if (!selection.set()) {
1054 selection.start = cursor;
1055 selection.end = cursor;
1058 // make sure that the depth behind the selection are restored, too
1059 Paragraph * endpar = selection.end.par()->next();
1060 Paragraph * undoendpar = endpar;
1062 if (endpar && endpar->getDepth()) {
1063 while (endpar && endpar->getDepth()) {
1064 endpar = endpar->next();
1065 undoendpar = endpar;
1069 // because of parindents etc.
1070 endpar = endpar->next();
1073 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1076 Paragraph * tmppar = selection.end.par();
1077 while (tmppar != selection.start.par()->previous()) {
1078 setCursor(bview, tmppar, 0);
1079 status(bview, LyXText::NEED_MORE_REFRESH);
1080 refresh_row = cursor.row();
1081 refresh_y = cursor.y() - cursor.row()->baseline();
1082 cursor.par()->params().lineTop(line_top);
1083 cursor.par()->params().lineBottom(line_bottom);
1084 cursor.par()->params().pagebreakTop(pagebreak_top);
1085 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1086 cursor.par()->params().spaceTop(space_top);
1087 cursor.par()->params().spaceBottom(space_bottom);
1088 // does the layout allow the new alignment?
1089 if (align == LYX_ALIGN_LAYOUT)
1090 align = textclasslist
1091 .Style(bview->buffer()->params.textclass,
1092 cursor.par()->getLayout()).align;
1093 if (align & textclasslist
1094 .Style(bview->buffer()->params.textclass,
1095 cursor.par()->getLayout()).alignpossible) {
1096 if (align == textclasslist
1097 .Style(bview->buffer()->params.textclass,
1098 cursor.par()->getLayout()).align)
1099 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1101 cursor.par()->params().align(align);
1103 cursor.par()->setLabelWidthString(labelwidthstring);
1104 cursor.par()->params().noindent(noindent);
1105 tmppar = cursor.par()->previous();
1108 redoParagraphs(bview, selection.start, endpar);
1110 clearSelection(bview);
1111 setCursor(bview, selection.start.par(), selection.start.pos());
1112 selection.cursor = cursor;
1113 setCursor(bview, selection.end.par(), selection.end.pos());
1114 setSelection(bview);
1115 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1117 bview->updateInset(inset_owner, true);
1121 char loweralphaCounter(int n)
1123 if (n < 1 || n > 26)
1133 char alphaCounter(int n)
1135 if (n < 1 || n > 26)
1143 char hebrewCounter(int n)
1145 static const char hebrew[22] = {
1146 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1147 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1150 if (n < 1 || n > 22)
1158 string const romanCounter(int n)
1160 static char const * roman[20] = {
1161 "i", "ii", "iii", "iv", "v",
1162 "vi", "vii", "viii", "ix", "x",
1163 "xi", "xii", "xiii", "xiv", "xv",
1164 "xvi", "xvii", "xviii", "xix", "xx"
1166 if (n < 1 || n > 20)
1175 // set the counter of a paragraph. This includes the labels
1176 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1178 LyXLayout const & layout =
1179 textclasslist.Style(buf->params.textclass,
1182 LyXTextClass const & textclass =
1183 textclasslist.TextClass(buf->params.textclass);
1185 // copy the prev-counters to this one,
1186 // unless this is the first paragraph
1187 if (par->previous()) {
1188 for (int i = 0; i < 10; ++i) {
1189 par->setCounter(i, par->previous()->getFirstCounter(i));
1191 par->params().appendix(par->previous()->params().appendix());
1192 if (!par->params().appendix() && par->params().startOfAppendix()) {
1193 par->params().appendix(true);
1194 for (int i = 0; i < 10; ++i) {
1195 par->setCounter(i, 0);
1198 par->enumdepth = par->previous()->enumdepth;
1199 par->itemdepth = par->previous()->itemdepth;
1201 for (int i = 0; i < 10; ++i) {
1202 par->setCounter(i, 0);
1204 par->params().appendix(par->params().startOfAppendix());
1209 /* Maybe we have to increment the enumeration depth.
1210 * BUT, enumeration in a footnote is considered in isolation from its
1211 * surrounding paragraph so don't increment if this is the
1212 * first line of the footnote
1213 * AND, bibliographies can't have their depth changed ie. they
1214 * are always of depth 0
1217 && par->previous()->getDepth() < par->getDepth()
1218 && textclasslist.Style(buf->params.textclass,
1219 par->previous()->getLayout()
1220 ).labeltype == LABEL_COUNTER_ENUMI
1221 && par->enumdepth < 3
1222 && layout.labeltype != LABEL_BIBLIO) {
1226 // Maybe we have to decrement the enumeration depth, see note above
1228 && par->previous()->getDepth() > par->getDepth()
1229 && layout.labeltype != LABEL_BIBLIO) {
1230 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1231 par->setCounter(6 + par->enumdepth,
1232 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1233 /* reset the counters.
1234 * A depth change is like a breaking layout
1236 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1237 par->setCounter(i, 0);
1240 if (!par->params().labelString().empty()) {
1241 par->params().labelString(string());
1244 if (layout.margintype == MARGIN_MANUAL) {
1245 if (par->params().labelWidthString().empty()) {
1246 par->setLabelWidthString(layout.labelstring());
1249 par->setLabelWidthString(string());
1252 // is it a layout that has an automatic label?
1253 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1255 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1256 if (i >= 0 && i<= buf->params.secnumdepth) {
1257 par->incCounter(i); // increment the counter
1259 // Is there a label? Useful for Chapter layout
1260 if (!par->params().appendix()) {
1261 if (!layout.labelstring().empty())
1262 par->params().labelString(layout.labelstring());
1264 par->params().labelString(string());
1266 if (!layout.labelstring_appendix().empty())
1267 par->params().labelString(layout.labelstring_appendix());
1269 par->params().labelString(string());
1274 if (!par->params().appendix()) {
1275 switch (2 * LABEL_COUNTER_CHAPTER -
1276 textclass.maxcounter() + i) {
1277 case LABEL_COUNTER_CHAPTER:
1278 s << par->getCounter(i);
1280 case LABEL_COUNTER_SECTION:
1281 s << par->getCounter(i - 1) << '.'
1282 << par->getCounter(i);
1284 case LABEL_COUNTER_SUBSECTION:
1285 s << par->getCounter(i - 2) << '.'
1286 << par->getCounter(i - 1) << '.'
1287 << par->getCounter(i);
1289 case LABEL_COUNTER_SUBSUBSECTION:
1290 s << par->getCounter(i - 3) << '.'
1291 << par->getCounter(i - 2) << '.'
1292 << par->getCounter(i - 1) << '.'
1293 << par->getCounter(i);
1296 case LABEL_COUNTER_PARAGRAPH:
1297 s << par->getCounter(i - 4) << '.'
1298 << par->getCounter(i - 3) << '.'
1299 << par->getCounter(i - 2) << '.'
1300 << par->getCounter(i - 1) << '.'
1301 << par->getCounter(i);
1303 case LABEL_COUNTER_SUBPARAGRAPH:
1304 s << par->getCounter(i - 5) << '.'
1305 << par->getCounter(i - 4) << '.'
1306 << par->getCounter(i - 3) << '.'
1307 << par->getCounter(i - 2) << '.'
1308 << par->getCounter(i - 1) << '.'
1309 << par->getCounter(i);
1313 // Can this ever be reached? And in the
1314 // case it is, how can this be correct?
1316 s << par->getCounter(i) << '.';
1319 } else { // appendix
1320 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1321 case LABEL_COUNTER_CHAPTER:
1322 if (par->isRightToLeftPar(buf->params))
1323 s << hebrewCounter(par->getCounter(i));
1325 s << alphaCounter(par->getCounter(i));
1327 case LABEL_COUNTER_SECTION:
1328 if (par->isRightToLeftPar(buf->params))
1329 s << hebrewCounter(par->getCounter(i - 1));
1331 s << alphaCounter(par->getCounter(i - 1));
1334 << par->getCounter(i);
1337 case LABEL_COUNTER_SUBSECTION:
1338 if (par->isRightToLeftPar(buf->params))
1339 s << hebrewCounter(par->getCounter(i - 2));
1341 s << alphaCounter(par->getCounter(i - 2));
1344 << par->getCounter(i-1) << '.'
1345 << par->getCounter(i);
1348 case LABEL_COUNTER_SUBSUBSECTION:
1349 if (par->isRightToLeftPar(buf->params))
1350 s << hebrewCounter(par->getCounter(i-3));
1352 s << alphaCounter(par->getCounter(i-3));
1355 << par->getCounter(i-2) << '.'
1356 << par->getCounter(i-1) << '.'
1357 << par->getCounter(i);
1360 case LABEL_COUNTER_PARAGRAPH:
1361 if (par->isRightToLeftPar(buf->params))
1362 s << hebrewCounter(par->getCounter(i-4));
1364 s << alphaCounter(par->getCounter(i-4));
1367 << par->getCounter(i-3) << '.'
1368 << par->getCounter(i-2) << '.'
1369 << par->getCounter(i-1) << '.'
1370 << par->getCounter(i);
1373 case LABEL_COUNTER_SUBPARAGRAPH:
1374 if (par->isRightToLeftPar(buf->params))
1375 s << hebrewCounter(par->getCounter(i-5));
1377 s << alphaCounter(par->getCounter(i-5));
1380 << par->getCounter(i-4) << '.'
1381 << par->getCounter(i-3) << '.'
1382 << par->getCounter(i-2) << '.'
1383 << par->getCounter(i-1) << '.'
1384 << par->getCounter(i);
1388 // Can this ever be reached? And in the
1389 // case it is, how can this be correct?
1391 s << par->getCounter(i) << '.';
1397 par->params().labelString(par->params().labelString() +s.str().c_str());
1398 // We really want to remove the c_str as soon as
1401 for (i++; i < 10; ++i) {
1402 // reset the following counters
1403 par->setCounter(i, 0);
1405 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1406 for (i++; i < 10; ++i) {
1407 // reset the following counters
1408 par->setCounter(i, 0);
1410 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1411 par->incCounter(i + par->enumdepth);
1412 int number = par->getCounter(i + par->enumdepth);
1416 switch (par->enumdepth) {
1418 if (par->isRightToLeftPar(buf->params))
1420 << hebrewCounter(number)
1424 << loweralphaCounter(number)
1428 if (par->isRightToLeftPar(buf->params))
1429 s << '.' << romanCounter(number);
1431 s << romanCounter(number) << '.';
1434 if (par->isRightToLeftPar(buf->params))
1436 << alphaCounter(number);
1438 s << alphaCounter(number)
1442 if (par->isRightToLeftPar(buf->params))
1449 par->params().labelString(s.str().c_str());
1451 for (i += par->enumdepth + 1; i < 10; ++i) {
1452 // reset the following counters
1453 par->setCounter(i, 0);
1457 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1458 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1460 int number = par->getCounter(i);
1462 InsetCommandParams p( "bibitem" );
1463 par->bibkey = new InsetBibKey(p);
1465 par->bibkey->setCounter(number);
1466 par->params().labelString(layout.labelstring());
1468 // In biblio should't be following counters but...
1470 string s = layout.labelstring();
1472 // the caption hack:
1473 if (layout.labeltype == LABEL_SENSITIVE) {
1474 bool isOK (par->inInset() && par->inInset()->owner() &&
1475 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1478 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1480 = floatList.getType(tmp->type());
1481 // We should get the correct number here too.
1482 s = fl.name() + " #:";
1484 /* par->SetLayout(0);
1485 s = layout->labelstring; */
1486 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1487 ? " :úåòîùî øñç" : "Senseless: ";
1490 par->params().labelString(s);
1492 /* reset the enumeration counter. They are always resetted
1493 * when there is any other layout between */
1494 for (int i = 6 + par->enumdepth; i < 10; ++i)
1495 par->setCounter(i, 0);
1500 // Updates all counters BEHIND the row. Changed paragraphs
1501 // with a dynamic left margin will be rebroken.
1502 void LyXText::updateCounters(BufferView * bview, Row * row) const
1510 par = row->par()->next();
1514 while (row->par() != par)
1517 setCounter(bview->buffer(), par);
1519 // now check for the headline layouts. remember that they
1520 // have a dynamic left margin
1521 if ((textclasslist.Style(bview->buffer()->params.textclass,
1522 par->layout).margintype == MARGIN_DYNAMIC
1523 || textclasslist.Style(bview->buffer()->params.textclass,
1524 par->layout).labeltype == LABEL_SENSITIVE)) {
1526 // Rebreak the paragraph
1527 removeParagraph(row);
1528 appendParagraph(bview, row);
1535 void LyXText::insertInset(BufferView * bview, Inset * inset)
1537 if (!cursor.par()->insertInsetAllowed(inset))
1539 setUndo(bview, Undo::INSERT,
1540 cursor.par(), cursor.par()->next());
1541 cursor.par()->insertInset(cursor.pos(), inset);
1542 // Just to rebreak and refresh correctly.
1543 // The character will not be inserted a second time
1544 insertChar(bview, Paragraph::META_INSET);
1546 // If we enter a highly editable inset the cursor should be to before
1547 // the inset. This couldn't happen before as Undo was not handled inside
1548 // inset now after the Undo LyX tries to call inset->Edit(...) again
1549 // and cannot do this as the cursor is behind the inset and GetInset
1550 // does not return the inset!
1551 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1552 cursorLeft(bview, true);
1558 void LyXText::copyEnvironmentType()
1560 copylayouttype = cursor.par()->getLayout();
1564 void LyXText::pasteEnvironmentType(BufferView * bview)
1566 setLayout(bview, copylayouttype);
1570 void LyXText::cutSelection(BufferView * bview, bool doclear)
1572 // Stuff what we got on the clipboard. Even if there is no selection.
1574 // There is a problem with having the stuffing here in that the
1575 // larger the selection the slower LyX will get. This can be
1576 // solved by running the line below only when the selection has
1577 // finished. The solution used currently just works, to make it
1578 // faster we need to be more clever and probably also have more
1579 // calls to stuffClipboard. (Lgb)
1580 bview->stuffClipboard(selectionAsString(bview->buffer()));
1582 // This doesn't make sense, if there is no selection
1583 if (!selection.set())
1586 // OK, we have a selection. This is always between selection.start
1587 // and selection.end
1589 // make sure that the depth behind the selection are restored, too
1590 Paragraph * endpar = selection.end.par()->next();
1591 Paragraph * undoendpar = endpar;
1593 if (endpar && endpar->getDepth()) {
1594 while (endpar && endpar->getDepth()) {
1595 endpar = endpar->next();
1596 undoendpar = endpar;
1598 } else if (endpar) {
1599 endpar = endpar->next(); // because of parindents etc.
1602 setUndo(bview, Undo::DELETE,
1603 selection.start.par(), undoendpar);
1605 // there are two cases: cut only within one paragraph or
1606 // more than one paragraph
1607 if (selection.start.par() == selection.end.par()) {
1608 // only within one paragraph
1609 endpar = selection.end.par();
1610 int pos = selection.end.pos();
1611 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1612 selection.start.pos(), pos,
1613 bview->buffer()->params.textclass, doclear);
1614 selection.end.pos(pos);
1616 endpar = selection.end.par();
1617 int pos = selection.end.pos();
1618 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1619 selection.start.pos(), pos,
1620 bview->buffer()->params.textclass, doclear);
1622 selection.end.par(endpar);
1623 selection.end.pos(pos);
1624 cursor.pos(selection.end.pos());
1626 endpar = endpar->next();
1628 // sometimes necessary
1630 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1632 redoParagraphs(bview, selection.start, endpar);
1634 // cutSelection can invalidate the cursor so we need to set
1636 cursor = selection.start;
1638 // need a valid cursor. (Lgb)
1639 clearSelection(bview);
1641 setCursor(bview, cursor.par(), cursor.pos());
1642 selection.cursor = cursor;
1643 updateCounters(bview, cursor.row());
1647 void LyXText::copySelection(BufferView * bview)
1649 // Stuff what we got on the clipboard. Even if there is no selection.
1651 // There is a problem with having the stuffing here in that the
1652 // larger the selection the slower LyX will get. This can be
1653 // solved by running the line below only when the selection has
1654 // finished. The solution used currently just works, to make it
1655 // faster we need to be more clever and probably also have more
1656 // calls to stuffClipboard. (Lgb)
1657 bview->stuffClipboard(selectionAsString(bview->buffer()));
1659 // this doesnt make sense, if there is no selection
1660 if (!selection.set())
1663 // ok we have a selection. This is always between selection.start
1664 // and sel_end cursor
1666 // copy behind a space if there is one
1667 while (selection.start.par()->size() > selection.start.pos()
1668 && selection.start.par()->isLineSeparator(selection.start.pos())
1669 && (selection.start.par() != selection.end.par()
1670 || selection.start.pos() < selection.end.pos()))
1671 selection.start.pos(selection.start.pos() + 1);
1673 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1674 selection.start.pos(), selection.end.pos(),
1675 bview->buffer()->params.textclass);
1679 void LyXText::pasteSelection(BufferView * bview)
1681 // this does not make sense, if there is nothing to paste
1682 if (!CutAndPaste::checkPastePossible(cursor.par()))
1685 setUndo(bview, Undo::INSERT,
1686 cursor.par(), cursor.par()->next());
1689 Paragraph * actpar = cursor.par();
1691 int pos = cursor.pos();
1692 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1693 bview->buffer()->params.textclass);
1695 redoParagraphs(bview, cursor, endpar);
1697 setCursor(bview, cursor.par(), cursor.pos());
1698 clearSelection(bview);
1700 selection.cursor = cursor;
1701 setCursor(bview, actpar, pos);
1702 setSelection(bview);
1703 updateCounters(bview, cursor.row());
1707 // returns a pointer to the very first Paragraph
1708 Paragraph * LyXText::firstParagraph() const
1710 return ownerParagraph();
1714 // sets the selection over the number of characters of string, no check!!
1715 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1720 selection.cursor = cursor;
1721 for (string::size_type i = 0; i < str.length(); ++i)
1723 setSelection(bview);
1727 // simple replacing. The font of the first selected character is used
1728 void LyXText::replaceSelectionWithString(BufferView * bview,
1731 setCursorParUndo(bview);
1734 if (!selection.set()) { // create a dummy selection
1735 selection.end = cursor;
1736 selection.start = cursor;
1739 // Get font setting before we cut
1740 Paragraph::size_type pos = selection.end.pos();
1741 LyXFont const font = selection.start.par()
1742 ->getFontSettings(bview->buffer()->params,
1743 selection.start.pos());
1745 // Insert the new string
1746 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1747 selection.end.par()->insertChar(pos, (*cit), font);
1751 // Cut the selection
1752 cutSelection(bview);
1758 // needed to insert the selection
1759 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1761 Paragraph * par = cursor.par();
1762 Paragraph::size_type pos = cursor.pos();
1763 Paragraph * endpar = cursor.par()->next();
1765 setCursorParUndo(bview);
1767 bool isEnvironment =
1768 textclasslist.Style(bview->buffer()->params.textclass,
1769 cursor.par()->getLayout()).isEnvironment();
1771 textclasslist.Style(bview->buffer()->params.textclass,
1772 cursor.par()->getLayout()).free_spacing;
1774 textclasslist.Style(bview->buffer()->params.textclass,
1775 cursor.par()->getLayout()).keepempty;
1777 // only to be sure, should not be neccessary
1778 clearSelection(bview);
1780 // insert the string, don't insert doublespace
1781 bool space_inserted = true;
1782 for(string::const_iterator cit = str.begin();
1783 cit != str.end(); ++cit) {
1785 if (par->size() || keepempty) {
1786 par->breakParagraph(bview->buffer()->params,
1787 pos, isEnvironment);
1790 space_inserted = true;
1794 // do not insert consecutive spaces if !free_spacing
1795 } else if ((*cit == ' ' || *cit == '\t')
1796 && space_inserted && !free_spacing) {
1798 } else if (*cit == '\t') {
1799 if (!free_spacing) {
1800 // tabs are like spaces here
1801 par->insertChar(pos, ' ',
1804 space_inserted = true;
1806 const Paragraph::value_type nb = 8 - pos % 8;
1807 for (Paragraph::size_type a = 0;
1809 par->insertChar(pos, ' ',
1813 space_inserted = true;
1815 } else if (!IsPrintable(*cit)) {
1816 // Ignore unprintables
1819 // just insert the character
1820 par->insertChar(pos, *cit, current_font);
1822 space_inserted = (*cit == ' ');
1827 redoParagraphs(bview, cursor, endpar);
1828 setCursor(bview, cursor.par(), cursor.pos());
1829 selection.cursor = cursor;
1830 setCursor(bview, par, pos);
1831 setSelection(bview);
1835 // turns double-CR to single CR, others where converted into one
1836 // blank. Then InsertStringAsLines is called
1837 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1839 string linestr(str);
1840 bool newline_inserted = false;
1841 for (string::size_type i = 0; i < linestr.length(); ++i) {
1842 if (linestr[i] == '\n') {
1843 if (newline_inserted) {
1844 // we know that \r will be ignored by
1845 // InsertStringA. Of course, it is a dirty
1846 // trick, but it works...
1847 linestr[i - 1] = '\r';
1851 newline_inserted = true;
1853 } else if (IsPrintable(linestr[i])) {
1854 newline_inserted = false;
1857 insertStringAsLines(bview, linestr);
1861 bool LyXText::gotoNextInset(BufferView * bview,
1862 std::vector<Inset::Code> const & codes,
1863 string const & contents) const
1865 LyXCursor res = cursor;
1868 if (res.pos() < res.par()->size() - 1) {
1869 res.pos(res.pos() + 1);
1871 res.par(res.par()->next());
1875 } while (res.par() &&
1876 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1877 && (inset = res.par()->getInset(res.pos())) != 0
1878 && find(codes.begin(), codes.end(), inset->lyxCode())
1880 && (contents.empty() ||
1881 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1885 setCursor(bview, res.par(), res.pos());
1892 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1893 Paragraph::size_type pos)
1895 LyXCursor tmpcursor;
1898 Paragraph::size_type z;
1899 Row * row = getRow(par, pos, y);
1901 // is there a break one row above
1902 if (row->previous() && row->previous()->par() == row->par()) {
1903 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1904 if (z >= row->pos()) {
1905 // set the dimensions of the row above
1906 y -= row->previous()->height();
1908 refresh_row = row->previous();
1909 status(bview, LyXText::NEED_MORE_REFRESH);
1911 breakAgain(bview, row->previous());
1913 // set the cursor again. Otherwise
1914 // dangling pointers are possible
1915 setCursor(bview, cursor.par(), cursor.pos(),
1916 false, cursor.boundary());
1917 selection.cursor = cursor;
1922 int const tmpheight = row->height();
1923 Paragraph::size_type const tmplast = rowLast(row);
1927 breakAgain(bview, row);
1928 if (row->height() == tmpheight && rowLast(row) == tmplast)
1929 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1931 status(bview, LyXText::NEED_MORE_REFRESH);
1933 // check the special right address boxes
1934 if (textclasslist.Style(bview->buffer()->params.textclass,
1935 par->getLayout()).margintype
1936 == MARGIN_RIGHT_ADDRESS_BOX) {
1943 redoDrawingOfParagraph(bview, tmpcursor);
1946 // set the cursor again. Otherwise dangling pointers are possible
1947 // also set the selection
1949 if (selection.set()) {
1951 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1952 false, selection.cursor.boundary());
1953 selection.cursor = cursor;
1954 setCursorIntern(bview, selection.start.par(),
1955 selection.start.pos(),
1956 false, selection.start.boundary());
1957 selection.start = cursor;
1958 setCursorIntern(bview, selection.end.par(),
1959 selection.end.pos(),
1960 false, selection.end.boundary());
1961 selection.end = cursor;
1962 setCursorIntern(bview, last_sel_cursor.par(),
1963 last_sel_cursor.pos(),
1964 false, last_sel_cursor.boundary());
1965 last_sel_cursor = cursor;
1968 setCursorIntern(bview, cursor.par(), cursor.pos(),
1969 false, cursor.boundary());
1973 // returns false if inset wasn't found
1974 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1976 // first check the current paragraph
1977 int pos = cursor.par()->getPositionOfInset(inset);
1979 checkParagraph(bview, cursor.par(), pos);
1983 // check every paragraph
1985 Paragraph * par = firstParagraph();
1987 pos = par->getPositionOfInset(inset);
1989 checkParagraph(bview, par, pos);
1999 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2000 Paragraph::size_type pos,
2001 bool setfont, bool boundary) const
2003 LyXCursor old_cursor = cursor;
2004 setCursorIntern(bview, par, pos, setfont, boundary);
2005 deleteEmptyParagraphMechanism(bview, old_cursor);
2009 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2010 Paragraph::size_type pos, bool boundary) const
2014 cur.boundary(boundary);
2016 // get the cursor y position in text
2018 Row * row = getRow(par, pos, y);
2019 // y is now the beginning of the cursor row
2020 y += row->baseline();
2021 // y is now the cursor baseline
2024 // now get the cursors x position
2026 float fill_separator;
2028 float fill_label_hfill;
2029 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2031 Paragraph::size_type cursor_vpos = 0;
2032 Paragraph::size_type last = rowLastPrintable(row);
2034 if (pos > last + 1) // This shouldn't happen.
2036 else if (pos < row->pos())
2039 if (last < row->pos())
2040 cursor_vpos = row->pos();
2041 else if (pos > last && !boundary)
2042 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2043 ? row->pos() : last + 1;
2044 else if (pos > row->pos() &&
2045 (pos > last || boundary))
2046 /// Place cursor after char at (logical) position pos - 1
2047 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2048 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2050 /// Place cursor before char at (logical) position pos
2051 cursor_vpos = (bidi_level(pos) % 2 == 0)
2052 ? log2vis(pos) : log2vis(pos) + 1;
2054 Paragraph::size_type main_body =
2055 beginningOfMainBody(bview->buffer(), row->par());
2056 if ((main_body > 0) &&
2057 ((main_body-1 > last) ||
2058 !row->par()->isLineSeparator(main_body-1)))
2061 for (Paragraph::size_type vpos = row->pos();
2062 vpos < cursor_vpos; ++vpos) {
2063 pos = vis2log(vpos);
2064 if (main_body > 0 && pos == main_body - 1) {
2065 x += fill_label_hfill +
2066 lyxfont::width(textclasslist.Style(
2067 bview->buffer()->params.textclass,
2068 row->par()->getLayout())
2070 getFont(bview->buffer(), row->par(), -2));
2071 if (row->par()->isLineSeparator(main_body-1))
2072 x -= singleWidth(bview, row->par(),main_body-1);
2074 if (hfillExpansion(bview->buffer(), row, pos)) {
2075 x += singleWidth(bview, row->par(), pos);
2076 if (pos >= main_body)
2079 x += fill_label_hfill;
2080 } else if (row->par()->isSeparator(pos)) {
2081 x += singleWidth(bview, row->par(), pos);
2082 if (pos >= main_body)
2083 x += fill_separator;
2085 x += singleWidth(bview, row->par(), pos);
2094 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2095 Paragraph::size_type pos,
2096 bool setfont, bool boundary) const
2098 InsetText * it = static_cast<InsetText *>(par->inInset());
2099 if (it && (it != inset_owner)) {
2100 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2103 setCursor(bview, cursor, par, pos, boundary);
2105 setCurrentFont(bview);
2110 void LyXText::setCurrentFont(BufferView * bview) const
2112 Paragraph::size_type pos = cursor.pos();
2113 if (cursor.boundary() && pos > 0)
2117 if (pos == cursor.par()->size())
2119 else // potentional bug... BUG (Lgb)
2120 if (cursor.par()->isSeparator(pos)) {
2121 if (pos > cursor.row()->pos() &&
2122 bidi_level(pos) % 2 ==
2123 bidi_level(pos - 1) % 2)
2125 else if (pos + 1 < cursor.par()->size())
2131 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2132 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2134 if (cursor.pos() == cursor.par()->size() &&
2135 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2136 !cursor.boundary()) {
2137 Language const * lang =
2138 cursor.par()->getParLanguage(bview->buffer()->params);
2139 current_font.setLanguage(lang);
2140 current_font.setNumber(LyXFont::OFF);
2141 real_current_font.setLanguage(lang);
2142 real_current_font.setNumber(LyXFont::OFF);
2147 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2149 LyXCursor old_cursor = cursor;
2151 // Get the row first.
2153 Row * row = getRowNearY(y);
2154 cursor.par(row->par());
2157 int column = getColumnNearX(bview, row, x, bound);
2158 cursor.pos(row->pos() + column);
2160 cursor.y(y + row->baseline());
2162 cursor.boundary(bound);
2163 setCurrentFont(bview);
2164 deleteEmptyParagraphMechanism(bview, old_cursor);
2168 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2171 // Get the row first.
2173 Row * row = getRowNearY(y);
2175 int column = getColumnNearX(bview, row, x, bound);
2177 cur.par(row->par());
2178 cur.pos(row->pos() + column);
2180 cur.y(y + row->baseline());
2182 cur.boundary(bound);
2186 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2188 if (cursor.pos() > 0) {
2189 bool boundary = cursor.boundary();
2190 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2191 if (!internal && !boundary &&
2192 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2193 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2194 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2195 Paragraph * par = cursor.par()->previous();
2196 setCursor(bview, par, par->size());
2201 void LyXText::cursorRight(BufferView * bview, bool internal) const
2203 if (!internal && cursor.boundary() &&
2204 !cursor.par()->isNewline(cursor.pos()))
2205 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2206 else if (cursor.pos() < cursor.par()->size()) {
2207 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2209 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2210 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2211 } else if (cursor.par()->next())
2212 setCursor(bview, cursor.par()->next(), 0);
2216 void LyXText::cursorUp(BufferView * bview) const
2218 setCursorFromCoordinates(bview, cursor.x_fix(),
2219 cursor.y() - cursor.row()->baseline() - 1);
2223 void LyXText::cursorDown(BufferView * bview) const
2225 setCursorFromCoordinates(bview, cursor.x_fix(),
2226 cursor.y() - cursor.row()->baseline()
2227 + cursor.row()->height() + 1);
2231 void LyXText::cursorUpParagraph(BufferView * bview) const
2233 if (cursor.pos() > 0) {
2234 setCursor(bview, cursor.par(), 0);
2236 else if (cursor.par()->previous()) {
2237 setCursor(bview, cursor.par()->previous(), 0);
2242 void LyXText::cursorDownParagraph(BufferView * bview) const
2244 if (cursor.par()->next()) {
2245 setCursor(bview, cursor.par()->next(), 0);
2247 setCursor(bview, cursor.par(), cursor.par()->size());
2252 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2253 LyXCursor const & old_cursor) const
2255 // Would be wrong to delete anything if we have a selection.
2256 if (selection.set()) return;
2258 // We allow all kinds of "mumbo-jumbo" when freespacing.
2259 if (textclasslist.Style(bview->buffer()->params.textclass,
2260 old_cursor.par()->getLayout()).free_spacing)
2263 bool deleted = false;
2265 /* Ok I'll put some comments here about what is missing.
2266 I have fixed BackSpace (and thus Delete) to not delete
2267 double-spaces automagically. I have also changed Cut,
2268 Copy and Paste to hopefully do some sensible things.
2269 There are still some small problems that can lead to
2270 double spaces stored in the document file or space at
2271 the beginning of paragraphs. This happens if you have
2272 the cursor betwenn to spaces and then save. Or if you
2273 cut and paste and the selection have a space at the
2274 beginning and then save right after the paste. I am
2275 sure none of these are very hard to fix, but I will
2276 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2277 that I can get some feedback. (Lgb)
2280 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2281 // delete the LineSeparator.
2284 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2285 // delete the LineSeparator.
2288 // If the pos around the old_cursor were spaces, delete one of them.
2289 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2290 // Only if the cursor has really moved
2292 if (old_cursor.pos() > 0
2293 && old_cursor.pos() < old_cursor.par()->size()
2294 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2295 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2296 old_cursor.par()->erase(old_cursor.pos() - 1);
2297 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2299 if (old_cursor.par() == cursor.par() &&
2300 cursor.pos() > old_cursor.pos()) {
2301 setCursorIntern(bview, cursor.par(),
2304 setCursorIntern(bview, cursor.par(),
2310 // Do not delete empty paragraphs with keepempty set.
2311 if ((textclasslist.Style(bview->buffer()->params.textclass,
2312 old_cursor.par()->getLayout())).keepempty)
2315 LyXCursor tmpcursor;
2317 if (old_cursor.par() != cursor.par()) {
2318 if ((old_cursor.par()->size() == 0
2319 || (old_cursor.par()->size() == 1
2320 && old_cursor.par()->isLineSeparator(0)))) {
2321 // ok, we will delete anything
2323 // make sure that you do not delete any environments
2324 status(bview, LyXText::NEED_MORE_REFRESH);
2327 if (old_cursor.row()->previous()) {
2328 refresh_row = old_cursor.row()->previous();
2329 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2331 cursor = old_cursor; // that undo can restore the right cursor position
2332 Paragraph * endpar = old_cursor.par()->next();
2333 if (endpar && endpar->getDepth()) {
2334 while (endpar && endpar->getDepth()) {
2335 endpar = endpar->next();
2338 setUndo(bview, Undo::DELETE,
2344 removeRow(old_cursor.row());
2345 if (ownerParagraph() == old_cursor.par()) {
2346 ownerParagraph(ownerParagraph()->next());
2349 delete old_cursor.par();
2351 /* Breakagain the next par. Needed
2352 * because of the parindent that
2353 * can occur or dissappear. The
2354 * next row can change its height,
2355 * if there is another layout before */
2356 if (refresh_row->next()) {
2357 breakAgain(bview, refresh_row->next());
2358 updateCounters(bview, refresh_row);
2360 setHeightOfRow(bview, refresh_row);
2362 refresh_row = old_cursor.row()->next();
2363 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2366 cursor = old_cursor; // that undo can restore the right cursor position
2367 Paragraph * endpar = old_cursor.par()->next();
2368 if (endpar && endpar->getDepth()) {
2369 while (endpar && endpar->getDepth()) {
2370 endpar = endpar->next();
2373 setUndo(bview, Undo::DELETE,
2379 removeRow(old_cursor.row());
2381 if (ownerParagraph() == old_cursor.par()) {
2382 ownerParagraph(ownerParagraph()->next());
2385 delete old_cursor.par();
2387 /* Breakagain the next par. Needed
2388 because of the parindent that can
2389 occur or dissappear.
2390 The next row can change its height,
2391 if there is another layout before
2394 breakAgain(bview, refresh_row);
2395 updateCounters(bview, refresh_row->previous());
2401 setCursorIntern(bview, cursor.par(), cursor.pos());
2403 if (selection.cursor.par() == old_cursor.par()
2404 && selection.cursor.pos() == selection.cursor.pos()) {
2405 // correct selection
2406 selection.cursor = cursor;
2410 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2411 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2413 setCursorIntern(bview, cursor.par(), cursor.pos());
2414 selection.cursor = cursor;
2421 void LyXText::toggleAppendix(BufferView * bview)
2423 Paragraph * par = cursor.par();
2424 bool start = !par->params().startOfAppendix();
2426 // ensure that we have only one start_of_appendix in this document
2427 Paragraph * tmp = firstParagraph();
2428 for (; tmp; tmp = tmp->next()) {
2429 tmp->params().startOfAppendix(false);
2432 par->params().startOfAppendix(start);
2434 // we can set the refreshing parameters now
2435 status(bview, LyXText::NEED_MORE_REFRESH);
2437 refresh_row = 0; // not needed for full update
2438 updateCounters(bview, 0);
2439 setCursor(bview, cursor.par(), cursor.pos());
2443 Paragraph * LyXText::ownerParagraph() const
2446 return inset_owner->paragraph();
2448 return bv_owner->buffer()->paragraph;
2452 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2455 inset_owner->paragraph(p);
2457 bv_owner->buffer()->paragraph = p;
2462 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2464 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2465 if (op && op->inInset()) {
2466 static_cast<InsetText *>(op->inInset())->paragraph(p);
2469 inset_owner->paragraph(p);
2471 bv_owner->buffer()->paragraph = p;
2478 LyXText::text_status LyXText::status() const
2484 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2487 if ((status_ != NEED_MORE_REFRESH)
2488 || (status_ == NEED_MORE_REFRESH)
2489 && (st != NEED_VERY_LITTLE_REFRESH)) {
2491 if (inset_owner && st != UNCHANGED) {
2492 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2496 #warning Please tell what the intention is here.
2497 // The above does not make any sense, I changed it to what is here,
2498 // but it still does not make much sense. (Lgb)
2499 if ((status_ != NEED_MORE_REFRESH)
2500 || (status_ == NEED_MORE_REFRESH
2501 && st != NEED_VERY_LITTLE_REFRESH)) {
2503 if (inset_owner && st != UNCHANGED) {
2504 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);