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 = static_cast<UpdatableInset *>(inset);
189 uinset->setFont(bv, fnt, toggleall, true);
191 font = inset->convertFont(font);
195 LyXLayout const & layout =
196 textclasslist.Style(buf->params.textclass,
199 // Get concrete layout font to reduce against
202 if (pos < beginningOfMainBody(buf, par))
203 layoutfont = layout.labelfont;
205 layoutfont = layout.font;
207 // Realize against environment font information
208 if (par->getDepth()){
209 Paragraph * tp = par;
210 while (!layoutfont.resolved() && tp && tp->getDepth()) {
211 tp = tp->outerHook();
213 layoutfont.realize(textclasslist.
214 Style(buf->params.textclass,
215 tp->getLayout()).font);
219 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
221 // Now, reduce font against full layout font
222 font.reduce(layoutfont);
224 par->setFont(pos, font);
228 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
229 Paragraph::size_type pos, LyXFont const & fnt)
232 // Let the insets convert their font
233 if (par->getChar(pos) == Paragraph::META_INSET) {
234 font = par->getInset(pos)->convertFont(font);
237 LyXLayout const & layout =
238 textclasslist.Style(buf->params.textclass,
241 // Get concrete layout font to reduce against
244 if (pos < beginningOfMainBody(buf, par))
245 layoutfont = layout.labelfont;
247 layoutfont = layout.font;
249 // Realize against environment font information
250 if (par->getDepth()){
251 Paragraph * tp = par;
252 while (!layoutfont.resolved() && tp && tp->getDepth()) {
253 tp = tp->outerHook();
255 layoutfont.realize(textclasslist.
256 Style(buf->params.textclass,
257 tp->getLayout()).font);
261 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
263 // Now, reduce font against full layout font
264 font.reduce(layoutfont);
266 par->setFont(pos, font);
270 /* inserts a new row behind the specified row, increments
271 * the touched counters */
272 void LyXText::insertRow(Row * row, Paragraph * par,
273 Paragraph::size_type pos) const
275 Row * tmprow = new Row;
278 tmprow->next(firstrow);
281 tmprow->previous(row);
282 tmprow->next(row->next());
287 tmprow->next()->previous(tmprow);
289 if (tmprow->previous())
290 tmprow->previous()->next(tmprow);
298 ++number_of_rows; // one more row
302 // removes the row and reset the touched counters
303 void LyXText::removeRow(Row * row) const
305 /* this must not happen before the currentrow for clear reasons.
306 so the trick is just to set the current row onto the previous
309 getRow(row->par(), row->pos(), unused_y);
312 row->next()->previous(row->previous());
313 if (!row->previous()) {
314 firstrow = row->next();
316 row->previous()->next(row->next());
319 lastrow = row->previous();
321 height -= row->height(); // the text becomes smaller
324 --number_of_rows; // one row less
328 // remove all following rows of the paragraph of the specified row.
329 void LyXText::removeParagraph(Row * row) const
331 Paragraph * tmppar = row->par();
335 while (row && row->par() == tmppar) {
336 tmprow = row->next();
343 // insert the specified paragraph behind the specified row
344 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
347 insertRow(row, par, 0); /* insert a new row, starting
350 setCounter(bview->buffer(), par); // set the counters
352 // and now append the whole paragraph behind the new row
355 appendParagraph(bview, firstrow);
357 row->next()->height(0);
358 appendParagraph(bview, row->next());
363 /* used in setlayout */
364 // Asger is not sure we want to do this...
365 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
369 LyXLayout const & layout =
370 textclasslist.Style(buf->params.textclass, par->getLayout());
372 LyXFont layoutfont, tmpfont;
373 for (Paragraph::size_type pos = 0;
374 pos < par->size(); ++pos) {
375 if (pos < beginningOfMainBody(buf, par))
376 layoutfont = layout.labelfont;
378 layoutfont = layout.font;
380 tmpfont = par->getFontSettings(buf->params, pos);
381 tmpfont.reduce(layoutfont);
382 par->setFont(pos, tmpfont);
387 Paragraph * LyXText::setLayout(BufferView * bview,
388 LyXCursor & cur, LyXCursor & sstart_cur,
389 LyXCursor & send_cur,
390 LyXTextClass::size_type layout)
392 Paragraph * endpar = send_cur.par()->next();
393 Paragraph * undoendpar = endpar;
395 if (endpar && endpar->getDepth()) {
396 while (endpar && endpar->getDepth()) {
397 endpar = endpar->next();
401 endpar = endpar->next(); // because of parindents etc.
404 setUndo(bview, Undo::EDIT,
405 sstart_cur.par(), undoendpar);
407 /* ok we have a selection. This is always between sstart_cur
408 * and sel_end cursor */
411 LyXLayout const & lyxlayout =
412 textclasslist.Style(bview->buffer()->params.textclass, layout);
414 while (cur.par() != send_cur.par()) {
415 cur.par()->setLayout(layout);
416 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
417 Paragraph * fppar = cur.par();
418 fppar->params().spaceTop(lyxlayout.fill_top ?
419 VSpace(VSpace::VFILL)
420 : VSpace(VSpace::NONE));
421 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
422 VSpace(VSpace::VFILL)
423 : VSpace(VSpace::NONE));
424 if (lyxlayout.margintype == MARGIN_MANUAL)
425 cur.par()->setLabelWidthString(lyxlayout.labelstring());
426 if (lyxlayout.labeltype != LABEL_BIBLIO
428 delete fppar->bibkey;
431 cur.par(cur.par()->next());
433 cur.par()->setLayout(layout);
434 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
435 Paragraph * fppar = cur.par();
436 fppar->params().spaceTop(lyxlayout.fill_top ?
437 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
438 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
439 VSpace(VSpace::VFILL) : 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;
451 // set layout over selection and make a total rebreak of those paragraphs
452 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
454 LyXCursor tmpcursor = cursor; /* store the current cursor */
456 // if there is no selection just set the layout
457 // of the current paragraph */
458 if (!selection.set()) {
459 selection.start = cursor; // dummy selection
460 selection.end = cursor;
462 Paragraph * endpar = setLayout(bview, cursor, selection.start,
463 selection.end, layout);
464 redoParagraphs(bview, selection.start, endpar);
466 // we have to reset the selection, because the
467 // geometry could have changed
468 setCursor(bview, selection.start.par(),
469 selection.start.pos(), false);
470 selection.cursor = cursor;
471 setCursor(bview, selection.end.par(), selection.end.pos(),
473 updateCounters(bview, cursor.row());
474 clearSelection(bview);
476 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
480 // increment depth over selection and
481 // make a total rebreak of those paragraphs
482 void LyXText::incDepth(BufferView * bview)
484 // If there is no selection, just use the current paragraph
485 if (!selection.set()) {
486 selection.start = cursor; // dummy selection
487 selection.end = cursor;
490 // We end at the next paragraph with depth 0
491 Paragraph * endpar = selection.end.par()->next();
493 Paragraph * undoendpar = endpar;
495 if (endpar && endpar->getDepth()) {
496 while (endpar && endpar->getDepth()) {
497 endpar = endpar->next();
502 endpar = endpar->next(); // because of parindents etc.
505 setUndo(bview, Undo::EDIT,
506 selection.start.par(), undoendpar);
508 LyXCursor tmpcursor = cursor; // store the current cursor
510 // ok we have a selection. This is always between sel_start_cursor
511 // and sel_end cursor
512 cursor = selection.start;
514 bool anything_changed = false;
517 // NOTE: you can't change the depth of a bibliography entry
519 textclasslist.Style(bview->buffer()->params.textclass,
520 cursor.par()->getLayout()
521 ).labeltype != LABEL_BIBLIO) {
522 Paragraph * prev = cursor.par()->previous();
525 && (prev->getDepth() - cursor.par()->getDepth() > 0
526 || (prev->getDepth() == cursor.par()->getDepth()
527 && textclasslist.Style(bview->buffer()->params.textclass,
528 prev->getLayout()).isEnvironment()))) {
529 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
530 anything_changed = true;
533 if (cursor.par() == selection.end.par())
535 cursor.par(cursor.par()->next());
538 // if nothing changed set all depth to 0
539 if (!anything_changed) {
540 cursor = selection.start;
541 while (cursor.par() != selection.end.par()) {
542 cursor.par()->params().depth(0);
543 cursor.par(cursor.par()->next());
545 cursor.par()->params().depth(0);
548 redoParagraphs(bview, selection.start, endpar);
550 // we have to reset the selection, because the
551 // geometry could have changed
552 setCursor(bview, selection.start.par(), selection.start.pos());
553 selection.cursor = cursor;
554 setCursor(bview, selection.end.par(), selection.end.pos());
555 updateCounters(bview, cursor.row());
556 clearSelection(bview);
558 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
562 // decrement depth over selection and
563 // make a total rebreak of those paragraphs
564 void LyXText::decDepth(BufferView * bview)
566 // if there is no selection just set the layout
567 // of the current paragraph
568 if (!selection.set()) {
569 selection.start = cursor; // dummy selection
570 selection.end = cursor;
572 Paragraph * endpar = selection.end.par()->next();
573 Paragraph * undoendpar = endpar;
575 if (endpar && endpar->getDepth()) {
576 while (endpar && endpar->getDepth()) {
577 endpar = endpar->next();
581 endpar = endpar->next(); // because of parindents etc.
584 setUndo(bview, Undo::EDIT,
585 selection.start.par(), undoendpar);
587 LyXCursor tmpcursor = cursor; // store the current cursor
589 // ok we have a selection. This is always between sel_start_cursor
590 // and sel_end cursor
591 cursor = selection.start;
594 if (cursor.par()->params().depth())
595 cursor.par()->params().depth(cursor.par()->params().depth() - 1);
596 if (cursor.par() == selection.end.par())
598 cursor.par(cursor.par()->next());
601 redoParagraphs(bview, selection.start, endpar);
603 // we have to reset the selection, because the
604 // geometry could have changed
605 setCursor(bview, selection.start.par(),
606 selection.start.pos());
607 selection.cursor = cursor;
608 setCursor(bview, selection.end.par(), selection.end.pos());
609 updateCounters(bview, cursor.row());
610 clearSelection(bview);
612 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
616 // set font over selection and make a total rebreak of those paragraphs
617 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
619 // if there is no selection just set the current_font
620 if (!selection.set()) {
621 // Determine basis font
623 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
625 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
627 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
628 // Update current font
629 real_current_font.update(font,
630 bview->buffer()->params.language,
633 // Reduce to implicit settings
634 current_font = real_current_font;
635 current_font.reduce(layoutfont);
636 // And resolve it completely
637 real_current_font.realize(layoutfont);
641 LyXCursor tmpcursor = cursor; // store the current cursor
643 // ok we have a selection. This is always between sel_start_cursor
644 // and sel_end cursor
646 setUndo(bview, Undo::EDIT,
647 selection.start.par(),
648 selection.end.par()->next());
650 cursor = selection.start;
651 while (cursor.par() != selection.end.par() ||
652 (cursor.pos() < selection.end.pos())) {
653 if (cursor.pos() < cursor.par()->size()) {
654 // an open footnote should behave
656 setCharFont(bview, cursor.par(), cursor.pos(), font, toggleall);
657 cursor.pos(cursor.pos() + 1);
660 cursor.par(cursor.par()->next());
665 redoParagraphs(bview, selection.start, selection.end.par()->next());
667 // we have to reset the selection, because the
668 // geometry could have changed
669 setCursor(bview, selection.start.par(), selection.start.pos());
670 selection.cursor = cursor;
671 setCursor(bview, selection.end.par(), selection.end.pos());
672 clearSelection(bview);
674 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
675 tmpcursor.boundary());
679 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
681 Row * tmprow = cur.row();
682 int y = cur.y() - tmprow->baseline();
684 setHeightOfRow(bview, tmprow);
686 while (tmprow->previous()
687 && tmprow->previous()->par() == tmprow->par()) {
688 tmprow = tmprow->previous();
689 y -= tmprow->height();
690 setHeightOfRow(bview, tmprow);
693 // we can set the refreshing parameters now
694 status(bview, LyXText::NEED_MORE_REFRESH);
696 refresh_row = tmprow;
697 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
701 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
703 Row * tmprow = cur.row();
705 int y = cur.y() - tmprow->baseline();
706 setHeightOfRow(bview, tmprow);
708 while (tmprow->previous()
709 && tmprow->previous()->par() == tmprow->par()) {
710 tmprow = tmprow->previous();
711 y -= tmprow->height();
714 // we can set the refreshing parameters now
715 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
717 refresh_row = tmprow;
719 status(bview, LyXText::NEED_MORE_REFRESH);
720 setCursor(bview, cur.par(), cur.pos());
724 /* deletes and inserts again all paragaphs between the cursor
725 * and the specified par
726 * This function is needed after SetLayout and SetFont etc. */
727 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
728 Paragraph const * endpar) const
731 Paragraph * tmppar = 0;
732 Paragraph * first_phys_par = 0;
734 Row * tmprow = cur.row();
736 int y = cur.y() - tmprow->baseline();
738 if (!tmprow->previous()) {
739 // a trick/hack for UNDO
740 // Can somebody please tell me _why_ this solves
742 first_phys_par = firstParagraph();
744 first_phys_par = tmprow->par();
745 while (tmprow->previous()
746 && tmprow->previous()->par() == first_phys_par) {
747 tmprow = tmprow->previous();
748 y -= tmprow->height();
752 // we can set the refreshing parameters now
753 status(bview, LyXText::NEED_MORE_REFRESH);
755 refresh_row = tmprow->previous(); /* the real refresh row will
756 be deleted, so I store
760 tmppar = tmprow->next()->par();
763 while (tmppar != endpar) {
764 removeRow(tmprow->next());
766 tmppar = tmprow->next()->par();
771 // remove the first one
772 tmprow2 = tmprow; /* this is because tmprow->previous()
774 tmprow = tmprow->previous();
777 tmppar = first_phys_par;
781 insertParagraph(bview, tmppar, tmprow);
784 while (tmprow->next() && tmprow->next()->par() == tmppar)
785 tmprow = tmprow->next();
786 tmppar = tmppar->next();
788 } while (tmppar && tmppar != endpar);
790 // this is because of layout changes
792 refresh_y -= refresh_row->height();
793 setHeightOfRow(bview, refresh_row);
795 refresh_row = firstrow;
797 setHeightOfRow(bview, refresh_row);
800 if (tmprow && tmprow->next())
801 setHeightOfRow(bview, tmprow->next());
805 bool LyXText::fullRebreak(BufferView * bview)
811 if (need_break_row) {
812 breakAgain(bview, need_break_row);
820 /* important for the screen */
823 /* the cursor set functions have a special mechanism. When they
824 * realize, that you left an empty paragraph, they will delete it.
825 * They also delete the corresponding row */
827 // need the selection cursor:
828 void LyXText::setSelection(BufferView * bview)
830 bool const lsel = selection.set();
832 if (!selection.set()) {
833 last_sel_cursor = selection.cursor;
834 selection.start = selection.cursor;
835 selection.end = selection.cursor;
840 // first the toggling area
841 if (cursor.y() < last_sel_cursor.y()
842 || (cursor.y() == last_sel_cursor.y()
843 && cursor.x() < last_sel_cursor.x())) {
844 toggle_end_cursor = last_sel_cursor;
845 toggle_cursor = cursor;
847 toggle_end_cursor = cursor;
848 toggle_cursor = last_sel_cursor;
851 last_sel_cursor = cursor;
853 // and now the whole selection
855 if (selection.cursor.par() == cursor.par())
856 if (selection.cursor.pos() < cursor.pos()) {
857 selection.end = cursor;
858 selection.start = selection.cursor;
860 selection.end = selection.cursor;
861 selection.start = cursor;
863 else if (selection.cursor.y() < cursor.y() ||
864 (selection.cursor.y() == cursor.y() && selection.cursor.x() < cursor.x())) {
865 selection.end = cursor;
866 selection.start = selection.cursor;
869 selection.end = selection.cursor;
870 selection.start = cursor;
873 // a selection with no contents is not a selection
874 if (selection.start.par() == selection.end.par() &&
875 selection.start.pos() == selection.end.pos())
876 selection.set(false);
878 if (inset_owner && (selection.set() || lsel))
879 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
883 string const LyXText::selectionAsString(Buffer const * buffer) const
885 if (!selection.set()) return string();
888 // Special handling if the whole selection is within one paragraph
889 if (selection.start.par() == selection.end.par()) {
890 result += selection.start.par()->asString(buffer,
891 selection.start.pos(),
892 selection.end.pos());
896 // The selection spans more than one paragraph
898 // First paragraph in selection
899 result += selection.start.par()->asString(buffer,
900 selection.start.pos(),
901 selection.start.par()->size())
904 // The paragraphs in between (if any)
905 LyXCursor tmpcur(selection.start);
906 tmpcur.par(tmpcur.par()->next());
907 while (tmpcur.par() != selection.end.par()) {
908 result += tmpcur.par()->asString(buffer, 0, tmpcur.par()->size()) + "\n\n";
909 tmpcur.par(tmpcur.par()->next()); // Or NextAfterFootnote??
912 // Last paragraph in selection
913 result += selection.end.par()->asString(buffer, 0, selection.end.pos());
919 void LyXText::clearSelection(BufferView * /*bview*/) const
921 selection.set(false);
922 selection.mark(false);
923 selection.end = selection.start = cursor;
927 void LyXText::cursorHome(BufferView * bview) const
929 setCursor(bview, cursor.par(), cursor.row()->pos());
933 void LyXText::cursorEnd(BufferView * bview) const
935 if (!cursor.row()->next() || cursor.row()->next()->par() != cursor.row()->par())
936 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
938 if (cursor.par()->size() &&
939 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
940 || cursor.par()->isNewline(rowLast(cursor.row()))))
941 setCursor(bview, cursor.par(), rowLast(cursor.row()));
943 setCursor(bview,cursor.par(), rowLast(cursor.row()) + 1);
948 void LyXText::cursorTop(BufferView * bview) const
950 while (cursor.par()->previous())
951 cursor.par(cursor.par()->previous());
952 setCursor(bview, cursor.par(), 0);
956 void LyXText::cursorBottom(BufferView * bview) const
958 while (cursor.par()->next())
959 cursor.par(cursor.par()->next());
960 setCursor(bview, cursor.par(), cursor.par()->size());
964 void LyXText::toggleFree(BufferView * bview,
965 LyXFont const & font, bool toggleall)
967 // If the mask is completely neutral, tell user
968 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
969 // Could only happen with user style
970 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
974 // Try implicit word selection
975 // If there is a change in the language the implicit word selection
977 LyXCursor resetCursor = cursor;
978 bool implicitSelection = (font.language() == ignore_language
979 && font.number() == LyXFont::IGNORE)
980 ? selectWordWhenUnderCursor(bview) : false;
983 setFont(bview, font, toggleall);
985 /* Implicit selections are cleared afterwards and cursor is set to the
986 original position. */
987 if (implicitSelection) {
988 clearSelection(bview);
989 cursor = resetCursor;
990 setCursor(bview, cursor.par(), cursor.pos());
991 selection.cursor = cursor;
994 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
999 LyXText::beginningOfMainBody(Buffer const * buf,
1000 Paragraph const * par) const
1002 if (textclasslist.Style(buf->params.textclass,
1003 par->getLayout()).labeltype != LABEL_MANUAL)
1006 return par->beginningOfMainBody();
1010 /* the DTP switches for paragraphs. LyX will store them in the
1011 * first physicla paragraph. When a paragraph is broken, the top settings
1012 * rest, the bottom settings are given to the new one. So I can make shure,
1013 * they do not duplicate themself and you cannnot make dirty things with
1016 void LyXText::setParagraph(BufferView * bview,
1017 bool line_top, bool line_bottom,
1018 bool pagebreak_top, bool pagebreak_bottom,
1019 VSpace const & space_top,
1020 VSpace const & space_bottom,
1022 string labelwidthstring,
1025 LyXCursor tmpcursor = cursor;
1026 if (!selection.set()) {
1027 selection.start = cursor;
1028 selection.end = cursor;
1031 // make sure that the depth behind the selection are restored, too
1032 Paragraph * endpar = selection.end.par()->next();
1033 Paragraph * undoendpar = endpar;
1035 if (endpar && endpar->getDepth()) {
1036 while (endpar && endpar->getDepth()) {
1037 endpar = endpar->next();
1038 undoendpar = endpar;
1042 endpar = endpar->next(); // because of parindents etc.
1045 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1048 Paragraph * tmppar = selection.end.par();
1049 while (tmppar != selection.start.par()->previous()) {
1050 setCursor(bview, tmppar, 0);
1051 status(bview, LyXText::NEED_MORE_REFRESH);
1052 refresh_row = cursor.row();
1053 refresh_y = cursor.y() - cursor.row()->baseline();
1054 cursor.par()->params().lineTop(line_top);
1055 cursor.par()->params().lineBottom(line_bottom);
1056 cursor.par()->params().pagebreakTop(pagebreak_top);
1057 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1058 cursor.par()->params().spaceTop(space_top);
1059 cursor.par()->params().spaceBottom(space_bottom);
1060 // does the layout allow the new alignment?
1061 if (align == LYX_ALIGN_LAYOUT)
1062 align = textclasslist
1063 .Style(bview->buffer()->params.textclass,
1064 cursor.par()->getLayout()).align;
1065 if (align & textclasslist
1066 .Style(bview->buffer()->params.textclass,
1067 cursor.par()->getLayout()).alignpossible) {
1068 if (align == textclasslist
1069 .Style(bview->buffer()->params.textclass,
1070 cursor.par()->getLayout()).align)
1071 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1073 cursor.par()->params().align(align);
1075 cursor.par()->setLabelWidthString(labelwidthstring);
1076 cursor.par()->params().noindent(noindent);
1077 tmppar = cursor.par()->previous();
1080 redoParagraphs(bview, selection.start, endpar);
1082 clearSelection(bview);
1083 setCursor(bview, selection.start.par(), selection.start.pos());
1084 selection.cursor = cursor;
1085 setCursor(bview, selection.end.par(), selection.end.pos());
1086 setSelection(bview);
1087 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1089 bview->updateInset(inset_owner, true);
1093 char loweralphaCounter(int n)
1095 if (n < 1 || n > 26)
1105 char alphaCounter(int n)
1107 if (n < 1 || n > 26)
1115 char hebrewCounter(int n)
1117 static const char hebrew[22] = {
1118 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1119 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1120 '÷', 'ø', 'ù', 'ú'
1122 if (n < 1 || n > 22)
1130 string const romanCounter(int n)
1132 static char const * roman[20] = {
1133 "i", "ii", "iii", "iv", "v",
1134 "vi", "vii", "viii", "ix", "x",
1135 "xi", "xii", "xiii", "xiv", "xv",
1136 "xvi", "xvii", "xviii", "xix", "xx"
1138 if (n < 1 || n > 20)
1147 // set the counter of a paragraph. This includes the labels
1148 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1150 LyXLayout const & layout =
1151 textclasslist.Style(buf->params.textclass,
1154 LyXTextClass const & textclass =
1155 textclasslist.TextClass(buf->params.textclass);
1157 // copy the prev-counters to this one, unless this is the first paragraph
1158 if (par->previous()) {
1159 for (int i = 0; i < 10; ++i) {
1160 par->setCounter(i, par->previous()->getFirstCounter(i));
1162 par->params().appendix(par->previous()->params().appendix());
1163 if (!par->params().appendix() && par->params().startOfAppendix()) {
1164 par->params().appendix(true);
1165 for (int i = 0; i < 10; ++i) {
1166 par->setCounter(i, 0);
1169 par->enumdepth = par->previous()->enumdepth;
1170 par->itemdepth = par->previous()->itemdepth;
1172 for (int i = 0; i < 10; ++i) {
1173 par->setCounter(i, 0);
1175 par->params().appendix(par->params().startOfAppendix());
1180 /* Maybe we have to increment the enumeration depth.
1181 * BUT, enumeration in a footnote is considered in isolation from its
1182 * surrounding paragraph so don't increment if this is the
1183 * first line of the footnote
1184 * AND, bibliographies can't have their depth changed ie. they
1185 * are always of depth 0
1188 && par->previous()->getDepth() < par->getDepth()
1189 && textclasslist.Style(buf->params.textclass,
1190 par->previous()->getLayout()
1191 ).labeltype == LABEL_COUNTER_ENUMI
1192 && par->enumdepth < 3
1193 && layout.labeltype != LABEL_BIBLIO) {
1197 /* Maybe we have to decrement the enumeration depth, see note above */
1199 && par->previous()->getDepth() > par->getDepth()
1200 && layout.labeltype != LABEL_BIBLIO) {
1201 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1202 par->setCounter(6 + par->enumdepth,
1203 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1204 /* reset the counters.
1205 * A depth change is like a breaking layout
1207 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1208 par->setCounter(i, 0);
1211 if (!par->params().labelString().empty()) {
1212 par->params().labelString(string());
1215 if (layout.margintype == MARGIN_MANUAL) {
1216 if (par->params().labelWidthString().empty()) {
1217 par->setLabelWidthString(layout.labelstring());
1220 par->setLabelWidthString(string());
1223 /* is it a layout that has an automatic label ? */
1224 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1226 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1227 if (i >= 0 && i<= buf->params.secnumdepth) {
1228 par->incCounter(i); // increment the counter
1230 // Is there a label? Useful for Chapter layout
1231 if (!par->params().appendix()) {
1232 if (!layout.labelstring().empty())
1233 par->params().labelString(layout.labelstring());
1235 par->params().labelString(string());
1237 if (!layout.labelstring_appendix().empty())
1238 par->params().labelString(layout.labelstring_appendix());
1240 par->params().labelString(string());
1243 std::ostringstream s;
1245 if (!par->params().appendix()) {
1246 switch (2 * LABEL_COUNTER_CHAPTER -
1247 textclass.maxcounter() + i) {
1248 case LABEL_COUNTER_CHAPTER:
1249 s << par->getCounter(i);
1251 case LABEL_COUNTER_SECTION:
1252 s << par->getCounter(i - 1) << '.'
1253 << par->getCounter(i);
1255 case LABEL_COUNTER_SUBSECTION:
1256 s << par->getCounter(i - 2) << '.'
1257 << par->getCounter(i - 1) << '.'
1258 << par->getCounter(i);
1260 case LABEL_COUNTER_SUBSUBSECTION:
1261 s << par->getCounter(i - 3) << '.'
1262 << par->getCounter(i - 2) << '.'
1263 << par->getCounter(i - 1) << '.'
1264 << par->getCounter(i);
1267 case LABEL_COUNTER_PARAGRAPH:
1268 s << par->getCounter(i - 4) << '.'
1269 << par->getCounter(i - 3) << '.'
1270 << par->getCounter(i - 2) << '.'
1271 << par->getCounter(i - 1) << '.'
1272 << par->getCounter(i);
1274 case LABEL_COUNTER_SUBPARAGRAPH:
1275 s << par->getCounter(i - 5) << '.'
1276 << par->getCounter(i - 4) << '.'
1277 << par->getCounter(i - 3) << '.'
1278 << par->getCounter(i - 2) << '.'
1279 << par->getCounter(i - 1) << '.'
1280 << par->getCounter(i);
1284 // Can this ever be reached? And in the
1285 // case it is, how can this be correct?
1287 s << par->getCounter(i) << '.';
1290 } else { // appendix
1291 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1292 case LABEL_COUNTER_CHAPTER:
1293 if (par->isRightToLeftPar(buf->params))
1294 s << hebrewCounter(par->getCounter(i));
1296 s << alphaCounter(par->getCounter(i));
1298 case LABEL_COUNTER_SECTION:
1299 if (par->isRightToLeftPar(buf->params))
1300 s << hebrewCounter(par->getCounter(i - 1));
1302 s << alphaCounter(par->getCounter(i - 1));
1305 << par->getCounter(i);
1308 case LABEL_COUNTER_SUBSECTION:
1309 if (par->isRightToLeftPar(buf->params))
1310 s << hebrewCounter(par->getCounter(i - 2));
1312 s << alphaCounter(par->getCounter(i - 2));
1315 << par->getCounter(i-1) << '.'
1316 << par->getCounter(i);
1319 case LABEL_COUNTER_SUBSUBSECTION:
1320 if (par->isRightToLeftPar(buf->params))
1321 s << hebrewCounter(par->getCounter(i-3));
1323 s << alphaCounter(par->getCounter(i-3));
1326 << par->getCounter(i-2) << '.'
1327 << par->getCounter(i-1) << '.'
1328 << par->getCounter(i);
1331 case LABEL_COUNTER_PARAGRAPH:
1332 if (par->isRightToLeftPar(buf->params))
1333 s << hebrewCounter(par->getCounter(i-4));
1335 s << alphaCounter(par->getCounter(i-4));
1338 << par->getCounter(i-3) << '.'
1339 << par->getCounter(i-2) << '.'
1340 << par->getCounter(i-1) << '.'
1341 << par->getCounter(i);
1344 case LABEL_COUNTER_SUBPARAGRAPH:
1345 if (par->isRightToLeftPar(buf->params))
1346 s << hebrewCounter(par->getCounter(i-5));
1348 s << alphaCounter(par->getCounter(i-5));
1351 << par->getCounter(i-4) << '.'
1352 << par->getCounter(i-3) << '.'
1353 << par->getCounter(i-2) << '.'
1354 << par->getCounter(i-1) << '.'
1355 << par->getCounter(i);
1359 // Can this ever be reached? And in the
1360 // case it is, how can this be correct?
1362 s << par->getCounter(i) << '.';
1368 par->params().labelString(par->params().labelString() +s.str().c_str());
1369 // We really want to remove the c_str as soon as
1372 for (i++; i < 10; ++i) {
1373 // reset the following counters
1374 par->setCounter(i, 0);
1376 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1377 for (i++; i < 10; ++i) {
1378 // reset the following counters
1379 par->setCounter(i, 0);
1381 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1382 par->incCounter(i + par->enumdepth);
1383 int number = par->getCounter(i + par->enumdepth);
1385 std::ostringstream s;
1387 switch (par->enumdepth) {
1389 if (par->isRightToLeftPar(buf->params))
1391 << hebrewCounter(number)
1395 << loweralphaCounter(number)
1399 if (par->isRightToLeftPar(buf->params))
1400 s << '.' << romanCounter(number);
1402 s << romanCounter(number) << '.';
1405 if (par->isRightToLeftPar(buf->params))
1407 << alphaCounter(number);
1409 s << alphaCounter(number)
1413 if (par->isRightToLeftPar(buf->params))
1420 par->params().labelString(s.str().c_str());
1421 // we really want to get rid of that c_str()
1423 for (i += par->enumdepth + 1; i < 10; ++i)
1424 par->setCounter(i, 0); /* reset the following counters */
1427 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1428 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1430 int number = par->getCounter(i);
1432 InsetCommandParams p( "bibitem" );
1433 par->bibkey = new InsetBibKey(p);
1435 par->bibkey->setCounter(number);
1436 par->params().labelString(layout.labelstring());
1438 // In biblio should't be following counters but...
1440 string s = layout.labelstring();
1442 // the caption hack:
1443 if (layout.labeltype == LABEL_SENSITIVE) {
1444 bool isOK (par->InInset() && par->InInset()->owner() &&
1445 (par->InInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1448 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1450 = floatList.getType(tmp->type());
1451 // We should get the correct number here too.
1452 s = fl.name() + " #:";
1454 /* par->SetLayout(0);
1455 s = layout->labelstring; */
1456 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1457 ? " :úåòîùî øñç" : "Senseless: ";
1460 par->params().labelString(s);
1462 /* reset the enumeration counter. They are always resetted
1463 * when there is any other layout between */
1464 for (int i = 6 + par->enumdepth; i < 10; ++i)
1465 par->setCounter(i, 0);
1470 /* Updates all counters BEHIND the row. Changed paragraphs
1471 * with a dynamic left margin will be rebroken. */
1472 void LyXText::updateCounters(BufferView * bview, Row * row) const
1480 par = row->par()->next();
1484 while (row->par() != par)
1487 setCounter(bview->buffer(), par);
1489 /* now check for the headline layouts. remember that they
1490 * have a dynamic left margin */
1491 if ((textclasslist.Style(bview->buffer()->params.textclass,
1492 par->layout).margintype == MARGIN_DYNAMIC
1493 || textclasslist.Style(bview->buffer()->params.textclass,
1494 par->layout).labeltype == LABEL_SENSITIVE)) {
1496 /* Rebreak the paragraph */
1497 removeParagraph(row);
1498 appendParagraph(bview, row);
1505 /* insets an inset. */
1506 void LyXText::insertInset(BufferView * bview, Inset * inset)
1508 if (!cursor.par()->insertInsetAllowed(inset))
1510 setUndo(bview, Undo::INSERT,
1511 cursor.par(), cursor.par()->next());
1512 cursor.par()->insertInset(cursor.pos(), inset);
1513 insertChar(bview, Paragraph::META_INSET); /* just to rebreak and refresh correctly.
1514 * The character will not be inserted a
1517 // If we enter a highly editable inset the cursor should be to before
1518 // the inset. This couldn't happen before as Undo was not handled inside
1519 // inset now after the Undo LyX tries to call inset->Edit(...) again
1520 // and cannot do this as the cursor is behind the inset and GetInset
1521 // does not return the inset!
1522 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1523 cursorLeft(bview, true);
1529 void LyXText::copyEnvironmentType()
1531 copylayouttype = cursor.par()->getLayout();
1535 void LyXText::pasteEnvironmentType(BufferView * bview)
1537 setLayout(bview, copylayouttype);
1541 void LyXText::cutSelection(BufferView * bview, bool doclear)
1543 // Stuff what we got on the clipboard. Even if there is no selection.
1545 // There is a problem with having the stuffing here in that the
1546 // larger the selection the slower LyX will get. This can be
1547 // solved by running the line below only when the selection has
1548 // finished. The solution used currently just works, to make it
1549 // faster we need to be more clever and probably also have more
1550 // calls to stuffClipboard. (Lgb)
1551 bview->stuffClipboard(selectionAsString(bview->buffer()));
1553 // This doesn't make sense, if there is no selection
1554 if (!selection.set())
1557 // OK, we have a selection. This is always between selection.start
1558 // and selection.end
1560 // make sure that the depth behind the selection are restored, too
1561 Paragraph * endpar = selection.end.par()->next();
1562 Paragraph * undoendpar = endpar;
1564 if (endpar && endpar->getDepth()) {
1565 while (endpar && endpar->getDepth()) {
1566 endpar = endpar->next();
1567 undoendpar = endpar;
1569 } else if (endpar) {
1570 endpar = endpar->next(); // because of parindents etc.
1573 setUndo(bview, Undo::DELETE,
1574 selection.start.par(), undoendpar);
1576 // there are two cases: cut only within one paragraph or
1577 // more than one paragraph
1578 if (selection.start.par() == selection.end.par()) {
1579 // only within one paragraph
1580 endpar = selection.end.par();
1581 int pos = selection.end.pos();
1582 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1583 selection.start.pos(), pos,
1584 bview->buffer()->params.textclass, doclear);
1585 selection.end.pos(pos);
1587 endpar = selection.end.par();
1588 int pos = selection.end.pos();
1589 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1590 selection.start.pos(), pos,
1591 bview->buffer()->params.textclass, doclear);
1593 selection.end.par(endpar);
1594 selection.end.pos(pos);
1595 cursor.pos(selection.end.pos());
1597 endpar = endpar->next();
1599 // sometimes necessary
1601 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1603 redoParagraphs(bview, selection.start, endpar);
1605 // cutSelection can invalidate the cursor so we need to set
1607 cursor = selection.start;
1609 // need a valid cursor. (Lgb)
1610 clearSelection(bview);
1612 setCursor(bview, cursor.par(), cursor.pos());
1613 selection.cursor = cursor;
1614 updateCounters(bview, cursor.row());
1618 void LyXText::copySelection(BufferView * bview)
1620 // Stuff what we got on the clipboard. Even if there is no selection.
1622 // There is a problem with having the stuffing here in that the
1623 // larger the selection the slower LyX will get. This can be
1624 // solved by running the line below only when the selection has
1625 // finished. The solution used currently just works, to make it
1626 // faster we need to be more clever and probably also have more
1627 // calls to stuffClipboard. (Lgb)
1628 bview->stuffClipboard(selectionAsString(bview->buffer()));
1630 // this doesnt make sense, if there is no selection
1631 if (!selection.set())
1634 // ok we have a selection. This is always between selection.start
1635 // and sel_end cursor
1637 // copy behind a space if there is one
1638 while (selection.start.par()->size() > selection.start.pos()
1639 && selection.start.par()->isLineSeparator(selection.start.pos())
1640 && (selection.start.par() != selection.end.par()
1641 || selection.start.pos() < selection.end.pos()))
1642 selection.start.pos(selection.start.pos() + 1);
1644 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1645 selection.start.pos(), selection.end.pos(),
1646 bview->buffer()->params.textclass);
1650 void LyXText::pasteSelection(BufferView * bview)
1652 // this does not make sense, if there is nothing to paste
1653 if (!CutAndPaste::checkPastePossible(cursor.par()))
1656 setUndo(bview, Undo::INSERT,
1657 cursor.par(), cursor.par()->next());
1660 Paragraph * actpar = cursor.par();
1662 int pos = cursor.pos();
1663 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1664 bview->buffer()->params.textclass);
1666 redoParagraphs(bview, cursor, endpar);
1668 setCursor(bview, cursor.par(), cursor.pos());
1669 clearSelection(bview);
1671 selection.cursor = cursor;
1672 setCursor(bview, actpar, pos);
1673 setSelection(bview);
1674 updateCounters(bview, cursor.row());
1678 // returns a pointer to the very first Paragraph
1679 Paragraph * LyXText::firstParagraph() const
1681 return ownerParagraph();
1685 // sets the selection over the number of characters of string, no check!!
1686 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1691 selection.cursor = cursor;
1692 for (string::size_type i = 0; i < str.length(); ++i)
1694 setSelection(bview);
1698 // simple replacing. The font of the first selected character is used
1699 void LyXText::replaceSelectionWithString(BufferView * bview,
1702 setCursorParUndo(bview);
1705 if (!selection.set()) { // create a dummy selection
1706 selection.end = cursor;
1707 selection.start = cursor;
1710 // Get font setting before we cut
1711 Paragraph::size_type pos = selection.end.pos();
1712 LyXFont const font = selection.start.par()
1713 ->getFontSettings(bview->buffer()->params,
1714 selection.start.pos());
1716 // Insert the new string
1717 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1718 selection.end.par()->insertChar(pos, (*cit), font);
1722 // Cut the selection
1723 cutSelection(bview);
1729 // needed to insert the selection
1730 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1732 Paragraph * par = cursor.par();
1733 Paragraph::size_type pos = cursor.pos();
1734 Paragraph * endpar = cursor.par()->next();
1736 setCursorParUndo(bview);
1738 bool isEnvironment =
1739 textclasslist.Style(bview->buffer()->params.textclass,
1740 cursor.par()->getLayout()).isEnvironment();
1742 textclasslist.Style(bview->buffer()->params.textclass,
1743 cursor.par()->getLayout()).free_spacing;
1745 textclasslist.Style(bview->buffer()->params.textclass,
1746 cursor.par()->getLayout()).keepempty;
1748 // only to be sure, should not be neccessary
1749 clearSelection(bview);
1751 // insert the string, don't insert doublespace
1752 bool space_inserted = true;
1753 for(string::const_iterator cit = str.begin();
1754 cit != str.end(); ++cit) {
1756 if (par->size() || keepempty) {
1757 par->breakParagraph(bview->buffer()->params,
1758 pos, isEnvironment);
1761 space_inserted = true;
1765 // do not insert consecutive spaces if !free_spacing
1766 } else if ((*cit == ' ' || *cit == '\t')
1767 && space_inserted && !free_spacing) {
1769 } else if (*cit == '\t') {
1770 if (!free_spacing) {
1771 // tabs are like spaces here
1772 par->insertChar(pos, ' ',
1775 space_inserted = true;
1777 const Paragraph::value_type nb = 8 - pos % 8;
1778 for (Paragraph::size_type a = 0;
1780 par->insertChar(pos, ' ',
1784 space_inserted = true;
1786 } else if (!IsPrintable(*cit)) {
1787 // Ignore unprintables
1790 // just insert the character
1791 par->insertChar(pos, *cit, current_font);
1793 space_inserted = (*cit == ' ');
1798 redoParagraphs(bview, cursor, endpar);
1799 setCursor(bview, cursor.par(), cursor.pos());
1800 selection.cursor = cursor;
1801 setCursor(bview, par, pos);
1802 setSelection(bview);
1806 /* turns double-CR to single CR, others where converted into one
1807 blank. Then InsertStringAsLines is called */
1808 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1810 string linestr(str);
1811 bool newline_inserted = false;
1812 for (string::size_type i = 0; i < linestr.length(); ++i) {
1813 if (linestr[i] == '\n') {
1814 if (newline_inserted) {
1815 // we know that \r will be ignored by
1816 // InsertStringA. Of course, it is a dirty
1817 // trick, but it works...
1818 linestr[i - 1] = '\r';
1822 newline_inserted = true;
1824 } else if (IsPrintable(linestr[i])) {
1825 newline_inserted = false;
1828 insertStringAsLines(bview, linestr);
1832 bool LyXText::gotoNextInset(BufferView * bview,
1833 std::vector<Inset::Code> const & codes,
1834 string const & contents) const
1836 LyXCursor res = cursor;
1839 if (res.pos() < res.par()->size() - 1) {
1840 res.pos(res.pos() + 1);
1842 res.par(res.par()->next());
1846 } while (res.par() &&
1847 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1848 && (inset = res.par()->getInset(res.pos())) != 0
1849 && find(codes.begin(), codes.end(), inset->lyxCode())
1851 && (contents.empty() ||
1852 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1856 setCursor(bview, res.par(), res.pos());
1863 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1864 Paragraph::size_type pos)
1866 LyXCursor tmpcursor;
1869 Paragraph::size_type z;
1870 Row * row = getRow(par, pos, y);
1872 // is there a break one row above
1873 if (row->previous() && row->previous()->par() == row->par()) {
1874 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1875 if (z >= row->pos()) {
1876 // set the dimensions of the row above
1877 y -= row->previous()->height();
1879 refresh_row = row->previous();
1880 status(bview, LyXText::NEED_MORE_REFRESH);
1882 breakAgain(bview, row->previous());
1884 // set the cursor again. Otherwise
1885 // dangling pointers are possible
1886 setCursor(bview, cursor.par(), cursor.pos(),
1887 false, cursor.boundary());
1888 selection.cursor = cursor;
1893 int const tmpheight = row->height();
1894 Paragraph::size_type const tmplast = rowLast(row);
1898 breakAgain(bview, row);
1899 if (row->height() == tmpheight && rowLast(row) == tmplast)
1900 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1902 status(bview, LyXText::NEED_MORE_REFRESH);
1904 // check the special right address boxes
1905 if (textclasslist.Style(bview->buffer()->params.textclass,
1906 par->getLayout()).margintype
1907 == MARGIN_RIGHT_ADDRESS_BOX) {
1914 redoDrawingOfParagraph(bview, tmpcursor);
1917 // set the cursor again. Otherwise dangling pointers are possible
1918 // also set the selection
1920 if (selection.set()) {
1922 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1923 false, selection.cursor.boundary());
1924 selection.cursor = cursor;
1925 setCursorIntern(bview, selection.start.par(),
1926 selection.start.pos(),
1927 false, selection.start.boundary());
1928 selection.start = cursor;
1929 setCursorIntern(bview, selection.end.par(),
1930 selection.end.pos(),
1931 false, selection.end.boundary());
1932 selection.end = cursor;
1933 setCursorIntern(bview, last_sel_cursor.par(),
1934 last_sel_cursor.pos(),
1935 false, last_sel_cursor.boundary());
1936 last_sel_cursor = cursor;
1939 setCursorIntern(bview, cursor.par(), cursor.pos(),
1940 false, cursor.boundary());
1944 // returns false if inset wasn't found
1945 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1947 // first check the current paragraph
1948 int pos = cursor.par()->getPositionOfInset(inset);
1950 checkParagraph(bview, cursor.par(), pos);
1954 // check every paragraph
1956 Paragraph * par = firstParagraph();
1958 pos = par->getPositionOfInset(inset);
1960 checkParagraph(bview, par, pos);
1970 void LyXText::setCursor(BufferView * bview, Paragraph * par,
1971 Paragraph::size_type pos,
1972 bool setfont, bool boundary) const
1974 LyXCursor old_cursor = cursor;
1975 setCursorIntern(bview, par, pos, setfont, boundary);
1976 deleteEmptyParagraphMechanism(bview, old_cursor);
1980 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
1981 Paragraph::size_type pos, bool boundary) const
1985 cur.boundary(boundary);
1987 /* get the cursor y position in text */
1989 Row * row = getRow(par, pos, y);
1990 /* y is now the beginning of the cursor row */
1991 y += row->baseline();
1992 /* y is now the cursor baseline */
1995 /* now get the cursors x position */
1997 float fill_separator;
1999 float fill_label_hfill;
2000 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2002 Paragraph::size_type cursor_vpos = 0;
2003 Paragraph::size_type last = rowLastPrintable(row);
2005 if (pos > last + 1) // This shouldn't happen.
2007 else if (pos < row->pos())
2010 if (last < row->pos())
2011 cursor_vpos = row->pos();
2012 else if (pos > last && !boundary)
2013 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2014 ? row->pos() : last + 1;
2015 else if (pos > row->pos() &&
2016 (pos > last || boundary))
2017 /// Place cursor after char at (logical) position pos - 1
2018 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2019 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2021 /// Place cursor before char at (logical) position pos
2022 cursor_vpos = (bidi_level(pos) % 2 == 0)
2023 ? log2vis(pos) : log2vis(pos) + 1;
2025 Paragraph::size_type main_body =
2026 beginningOfMainBody(bview->buffer(), row->par());
2027 if ((main_body > 0) &&
2028 ((main_body-1 > last) ||
2029 !row->par()->isLineSeparator(main_body-1)))
2032 for (Paragraph::size_type vpos = row->pos();
2033 vpos < cursor_vpos; ++vpos) {
2034 pos = vis2log(vpos);
2035 if (main_body > 0 && pos == main_body - 1) {
2036 x += fill_label_hfill +
2037 lyxfont::width(textclasslist.Style(
2038 bview->buffer()->params.textclass,
2039 row->par()->getLayout())
2041 getFont(bview->buffer(), row->par(), -2));
2042 if (row->par()->isLineSeparator(main_body-1))
2043 x -= singleWidth(bview, row->par(),main_body-1);
2045 if (hfillExpansion(bview->buffer(), row, pos)) {
2046 x += singleWidth(bview, row->par(), pos);
2047 if (pos >= main_body)
2050 x += fill_label_hfill;
2051 } else if (row->par()->isSeparator(pos)) {
2052 x += singleWidth(bview, row->par(), pos);
2053 if (pos >= main_body)
2054 x += fill_separator;
2056 x += singleWidth(bview, row->par(), pos);
2065 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2066 Paragraph::size_type pos,
2067 bool setfont, bool boundary) const
2069 InsetText * it = static_cast<InsetText *>(par->InInset());
2070 if (it && (it != inset_owner)) {
2071 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2074 setCursor(bview, cursor, par, pos, boundary);
2076 setCurrentFont(bview);
2081 void LyXText::setCurrentFont(BufferView * bview) const
2083 Paragraph::size_type pos = cursor.pos();
2084 if (cursor.boundary() && pos > 0)
2088 if (pos == cursor.par()->size())
2090 else // potentional bug... BUG (Lgb)
2091 if (cursor.par()->isSeparator(pos)) {
2092 if (pos > cursor.row()->pos() &&
2093 bidi_level(pos) % 2 ==
2094 bidi_level(pos - 1) % 2)
2096 else if (pos + 1 < cursor.par()->size())
2102 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2103 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2105 if (cursor.pos() == cursor.par()->size() &&
2106 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2107 !cursor.boundary()) {
2108 Language const * lang =
2109 cursor.par()->getParLanguage(bview->buffer()->params);
2110 current_font.setLanguage(lang);
2111 current_font.setNumber(LyXFont::OFF);
2112 real_current_font.setLanguage(lang);
2113 real_current_font.setNumber(LyXFont::OFF);
2118 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2120 LyXCursor old_cursor = cursor;
2122 /* get the row first */
2124 Row * row = getRowNearY(y);
2125 cursor.par(row->par());
2128 int column = getColumnNearX(bview, row, x, bound);
2129 cursor.pos(row->pos() + column);
2131 cursor.y(y + row->baseline());
2133 cursor.boundary(bound);
2134 setCurrentFont(bview);
2135 deleteEmptyParagraphMechanism(bview, old_cursor);
2139 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2142 /* get the row first */
2144 Row * row = getRowNearY(y);
2146 int column = getColumnNearX(bview, row, x, bound);
2148 cur.par(row->par());
2149 cur.pos(row->pos() + column);
2151 cur.y(y + row->baseline());
2153 cur.boundary(bound);
2157 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2159 if (cursor.pos() > 0) {
2160 bool boundary = cursor.boundary();
2161 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2162 if (!internal && !boundary &&
2163 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2164 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2165 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2166 Paragraph * par = cursor.par()->previous();
2167 setCursor(bview, par, par->size());
2172 void LyXText::cursorRight(BufferView * bview, bool internal) const
2174 if (!internal && cursor.boundary() &&
2175 !cursor.par()->isNewline(cursor.pos()))
2176 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2177 else if (cursor.pos() < cursor.par()->size()) {
2178 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2180 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2181 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2182 } else if (cursor.par()->next())
2183 setCursor(bview, cursor.par()->next(), 0);
2187 void LyXText::cursorUp(BufferView * bview) const
2189 setCursorFromCoordinates(bview, cursor.x_fix(),
2190 cursor.y() - cursor.row()->baseline() - 1);
2194 void LyXText::cursorDown(BufferView * bview) const
2196 setCursorFromCoordinates(bview, cursor.x_fix(),
2197 cursor.y() - cursor.row()->baseline()
2198 + cursor.row()->height() + 1);
2202 void LyXText::cursorUpParagraph(BufferView * bview) const
2204 if (cursor.pos() > 0) {
2205 setCursor(bview, cursor.par(), 0);
2207 else if (cursor.par()->previous()) {
2208 setCursor(bview, cursor.par()->previous(), 0);
2213 void LyXText::cursorDownParagraph(BufferView * bview) const
2215 if (cursor.par()->next()) {
2216 setCursor(bview, cursor.par()->next(), 0);
2218 setCursor(bview, cursor.par(), cursor.par()->size());
2223 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2224 LyXCursor const & old_cursor) const
2226 // Would be wrong to delete anything if we have a selection.
2227 if (selection.set()) return;
2229 // We allow all kinds of "mumbo-jumbo" when freespacing.
2230 if (textclasslist.Style(bview->buffer()->params.textclass,
2231 old_cursor.par()->getLayout()).free_spacing)
2234 bool deleted = false;
2236 /* Ok I'll put some comments here about what is missing.
2237 I have fixed BackSpace (and thus Delete) to not delete
2238 double-spaces automagically. I have also changed Cut,
2239 Copy and Paste to hopefully do some sensible things.
2240 There are still some small problems that can lead to
2241 double spaces stored in the document file or space at
2242 the beginning of paragraphs. This happens if you have
2243 the cursor betwenn to spaces and then save. Or if you
2244 cut and paste and the selection have a space at the
2245 beginning and then save right after the paste. I am
2246 sure none of these are very hard to fix, but I will
2247 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2248 that I can get some feedback. (Lgb)
2251 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2252 // delete the LineSeparator.
2255 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2256 // delete the LineSeparator.
2259 // If the pos around the old_cursor were spaces, delete one of them.
2260 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2261 // Only if the cursor has really moved
2263 if (old_cursor.pos() > 0
2264 && old_cursor.pos() < old_cursor.par()->size()
2265 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2266 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2267 old_cursor.par()->erase(old_cursor.pos() - 1);
2268 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2270 if (old_cursor.par() == cursor.par() &&
2271 cursor.pos() > old_cursor.pos()) {
2272 setCursorIntern(bview, cursor.par(),
2275 setCursorIntern(bview, cursor.par(),
2281 // Do not delete empty paragraphs with keepempty set.
2282 if ((textclasslist.Style(bview->buffer()->params.textclass,
2283 old_cursor.par()->getLayout())).keepempty)
2286 LyXCursor tmpcursor;
2288 if (old_cursor.par() != cursor.par()) {
2289 if ((old_cursor.par()->size() == 0
2290 || (old_cursor.par()->size() == 1
2291 && old_cursor.par()->isLineSeparator(0)))) {
2292 // ok, we will delete anything
2294 // make sure that you do not delete any environments
2295 status(bview, LyXText::NEED_MORE_REFRESH);
2298 if (old_cursor.row()->previous()) {
2299 refresh_row = old_cursor.row()->previous();
2300 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2302 cursor = old_cursor; // that undo can restore the right cursor position
2303 Paragraph * endpar = old_cursor.par()->next();
2304 if (endpar && endpar->getDepth()) {
2305 while (endpar && endpar->getDepth()) {
2306 endpar = endpar->next();
2309 setUndo(bview, Undo::DELETE,
2315 removeRow(old_cursor.row());
2316 if (ownerParagraph() == old_cursor.par()) {
2317 ownerParagraph(ownerParagraph()->next());
2320 delete old_cursor.par();
2322 /* Breakagain the next par. Needed
2323 * because of the parindent that
2324 * can occur or dissappear. The
2325 * next row can change its height,
2326 * if there is another layout before */
2327 if (refresh_row->next()) {
2328 breakAgain(bview, refresh_row->next());
2329 updateCounters(bview, refresh_row);
2331 setHeightOfRow(bview, refresh_row);
2333 refresh_row = old_cursor.row()->next();
2334 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2337 cursor = old_cursor; // that undo can restore the right cursor position
2338 Paragraph * endpar = old_cursor.par()->next();
2339 if (endpar && endpar->getDepth()) {
2340 while (endpar && endpar->getDepth()) {
2341 endpar = endpar->next();
2344 setUndo(bview, Undo::DELETE,
2350 removeRow(old_cursor.row());
2352 if (ownerParagraph() == old_cursor.par()) {
2353 ownerParagraph(ownerParagraph()->next());
2356 delete old_cursor.par();
2358 /* Breakagain the next par. Needed
2359 because of the parindent that can
2360 occur or dissappear.
2361 The next row can change its height,
2362 if there is another layout before
2365 breakAgain(bview, refresh_row);
2366 updateCounters(bview, refresh_row->previous());
2372 setCursorIntern(bview, cursor.par(), cursor.pos());
2374 if (selection.cursor.par() == old_cursor.par()
2375 && selection.cursor.pos() == selection.cursor.pos()) {
2376 // correct selection
2377 selection.cursor = cursor;
2381 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2382 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2384 setCursorIntern(bview, cursor.par(), cursor.pos());
2385 selection.cursor = cursor;
2392 Paragraph * LyXText::getParFromID(int id) const
2396 Paragraph * result = firstParagraph();
2397 Paragraph * ires = 0;
2398 while (result && result->id() != id) {
2399 if ((ires = result->getParFromID(id)))
2401 result = result->next();
2406 void LyXText::toggleAppendix(BufferView * bview)
2408 Paragraph * par = cursor.par();
2409 bool start = !par->params().startOfAppendix();
2411 // ensure that we have only one start_of_appendix in this document
2412 Paragraph * tmp = firstParagraph();
2413 for (; tmp; tmp = tmp->next())
2414 tmp->params().startOfAppendix(false);
2416 par->params().startOfAppendix(start);
2418 // we can set the refreshing parameters now
2419 status(bview, LyXText::NEED_MORE_REFRESH);
2421 refresh_row = 0; // not needed for full update
2422 updateCounters(bview, 0);
2423 setCursor(bview, cursor.par(), cursor.pos());
2427 Paragraph * LyXText::ownerParagraph() const
2430 return inset_owner->paragraph();
2432 return bv_owner->buffer()->paragraph;
2436 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2439 inset_owner->paragraph(p);
2441 bv_owner->buffer()->paragraph = p;
2445 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2447 Paragraph * op = getParFromID(id);
2448 if (op && op->InInset()) {
2449 static_cast<InsetText *>(op->InInset())->paragraph(p);
2452 inset_owner->paragraph(p);
2454 bv_owner->buffer()->paragraph = p;
2460 LyXText::text_status LyXText::status() const
2466 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2468 if ((status_ != NEED_MORE_REFRESH) ||
2469 (status_ == NEED_MORE_REFRESH) && (st != NEED_VERY_LITTLE_REFRESH))
2472 if (inset_owner && st != UNCHANGED)
2473 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);