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());
364 /* used in setlayout */
365 // Asger is not sure we want to do this...
366 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(), false);
472 updateCounters(bview, cursor.row());
473 clearSelection(bview);
475 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
479 // increment depth over selection and
480 // make a total rebreak of those paragraphs
481 void LyXText::incDepth(BufferView * bview)
483 // If there is no selection, just use the current paragraph
484 if (!selection.set()) {
485 selection.start = cursor; // dummy selection
486 selection.end = cursor;
489 // We end at the next paragraph with depth 0
490 Paragraph * endpar = selection.end.par()->next();
492 Paragraph * undoendpar = endpar;
494 if (endpar && endpar->getDepth()) {
495 while (endpar && endpar->getDepth()) {
496 endpar = endpar->next();
500 endpar = endpar->next(); // because of parindents etc.
503 setUndo(bview, Undo::EDIT,
504 selection.start.par(), undoendpar);
506 LyXCursor tmpcursor = cursor; // store the current cursor
508 // ok we have a selection. This is always between sel_start_cursor
509 // and sel_end cursor
510 cursor = selection.start;
512 bool anything_changed = false;
515 // NOTE: you can't change the depth of a bibliography entry
517 textclasslist.Style(bview->buffer()->params.textclass,
518 cursor.par()->getLayout()
519 ).labeltype != LABEL_BIBLIO) {
520 Paragraph * prev = cursor.par()->previous();
523 && (prev->getDepth() - cursor.par()->getDepth() > 0
524 || (prev->getDepth() == cursor.par()->getDepth()
525 && textclasslist.Style(bview->buffer()->params.textclass,
526 prev->getLayout()).isEnvironment()))) {
527 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
528 anything_changed = true;
531 if (cursor.par() == selection.end.par())
533 cursor.par(cursor.par()->next());
536 // if nothing changed set all depth to 0
537 if (!anything_changed) {
538 cursor = selection.start;
539 while (cursor.par() != selection.end.par()) {
540 cursor.par()->params().depth(0);
541 cursor.par(cursor.par()->next());
543 cursor.par()->params().depth(0);
546 redoParagraphs(bview, selection.start, endpar);
548 // we have to reset the selection, because the
549 // geometry could have changed
550 setCursor(bview, selection.start.par(), selection.start.pos());
551 selection.cursor = cursor;
552 setCursor(bview, selection.end.par(), selection.end.pos());
553 updateCounters(bview, cursor.row());
554 clearSelection(bview);
556 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
560 // decrement depth over selection and
561 // make a total rebreak of those paragraphs
562 void LyXText::decDepth(BufferView * bview)
564 // if there is no selection just set the layout
565 // of the current paragraph
566 if (!selection.set()) {
567 selection.start = cursor; // dummy selection
568 selection.end = cursor;
570 Paragraph * endpar = selection.end.par()->next();
571 Paragraph * undoendpar = endpar;
573 if (endpar && endpar->getDepth()) {
574 while (endpar && endpar->getDepth()) {
575 endpar = endpar->next();
579 endpar = endpar->next(); // because of parindents etc.
582 setUndo(bview, Undo::EDIT,
583 selection.start.par(), undoendpar);
585 LyXCursor tmpcursor = cursor; // store the current cursor
587 // ok we have a selection. This is always between sel_start_cursor
588 // and sel_end cursor
589 cursor = selection.start;
592 if (cursor.par()->params().depth()) {
593 cursor.par()->params()
594 .depth(cursor.par()->params().depth() - 1);
596 if (cursor.par() == selection.end.par()) {
599 cursor.par(cursor.par()->next());
602 redoParagraphs(bview, selection.start, endpar);
604 // we have to reset the selection, because the
605 // geometry could have changed
606 setCursor(bview, selection.start.par(),
607 selection.start.pos());
608 selection.cursor = cursor;
609 setCursor(bview, selection.end.par(), selection.end.pos());
610 updateCounters(bview, cursor.row());
611 clearSelection(bview);
613 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
617 // set font over selection and make a total rebreak of those paragraphs
618 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
620 // if there is no selection just set the current_font
621 if (!selection.set()) {
622 // Determine basis font
624 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
626 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
628 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
629 // Update current font
630 real_current_font.update(font,
631 bview->buffer()->params.language,
634 // Reduce to implicit settings
635 current_font = real_current_font;
636 current_font.reduce(layoutfont);
637 // And resolve it completely
638 real_current_font.realize(layoutfont);
642 LyXCursor tmpcursor = cursor; // store the current cursor
644 // ok we have a selection. This is always between sel_start_cursor
645 // and sel_end cursor
647 setUndo(bview, Undo::EDIT,
648 selection.start.par(),
649 selection.end.par()->next());
651 cursor = selection.start;
652 while (cursor.par() != selection.end.par() ||
653 (cursor.pos() < selection.end.pos())) {
654 if (cursor.pos() < cursor.par()->size()) {
655 // an open footnote should behave
657 setCharFont(bview, cursor.par(), cursor.pos(),
659 cursor.pos(cursor.pos() + 1);
662 cursor.par(cursor.par()->next());
667 redoParagraphs(bview, selection.start, selection.end.par()->next());
669 // we have to reset the selection, because the
670 // geometry could have changed
671 setCursor(bview, selection.start.par(), selection.start.pos());
672 selection.cursor = cursor;
673 setCursor(bview, selection.end.par(), selection.end.pos());
674 clearSelection(bview);
676 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
677 tmpcursor.boundary());
681 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
683 Row * tmprow = cur.row();
684 int y = cur.y() - tmprow->baseline();
686 setHeightOfRow(bview, tmprow);
688 while (tmprow->previous()
689 && tmprow->previous()->par() == tmprow->par()) {
690 tmprow = tmprow->previous();
691 y -= tmprow->height();
692 setHeightOfRow(bview, tmprow);
695 // we can set the refreshing parameters now
696 status(bview, LyXText::NEED_MORE_REFRESH);
698 refresh_row = tmprow;
699 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
703 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
705 Row * tmprow = cur.row();
707 int y = cur.y() - tmprow->baseline();
708 setHeightOfRow(bview, tmprow);
710 while (tmprow->previous()
711 && tmprow->previous()->par() == tmprow->par()) {
712 tmprow = tmprow->previous();
713 y -= tmprow->height();
716 // we can set the refreshing parameters now
717 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
719 refresh_row = tmprow;
721 status(bview, LyXText::NEED_MORE_REFRESH);
722 setCursor(bview, cur.par(), cur.pos());
726 // deletes and inserts again all paragaphs between the cursor
727 // and the specified par
728 // This function is needed after SetLayout and SetFont etc.
729 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
730 Paragraph const * endpar) const
733 Paragraph * tmppar = 0;
734 Paragraph * first_phys_par = 0;
736 Row * tmprow = cur.row();
738 int y = cur.y() - tmprow->baseline();
740 if (!tmprow->previous()) {
741 // a trick/hack for UNDO
742 // Can somebody please tell me _why_ this solves
744 first_phys_par = firstParagraph();
746 first_phys_par = tmprow->par();
747 while (tmprow->previous()
748 && tmprow->previous()->par() == first_phys_par) {
749 tmprow = tmprow->previous();
750 y -= tmprow->height();
754 // we can set the refreshing parameters now
755 status(bview, LyXText::NEED_MORE_REFRESH);
757 refresh_row = tmprow->previous(); /* the real refresh row will
758 be deleted, so I store
762 tmppar = tmprow->next()->par();
765 while (tmppar != endpar) {
766 removeRow(tmprow->next());
768 tmppar = tmprow->next()->par();
773 // remove the first one
774 tmprow2 = tmprow; /* this is because tmprow->previous()
776 tmprow = tmprow->previous();
779 tmppar = first_phys_par;
783 insertParagraph(bview, tmppar, tmprow);
787 while (tmprow->next()
788 && tmprow->next()->par() == tmppar) {
789 tmprow = tmprow->next();
791 tmppar = tmppar->next();
793 } while (tmppar && tmppar != endpar);
795 // this is because of layout changes
797 refresh_y -= refresh_row->height();
798 setHeightOfRow(bview, refresh_row);
800 refresh_row = firstrow;
802 setHeightOfRow(bview, refresh_row);
805 if (tmprow && tmprow->next())
806 setHeightOfRow(bview, tmprow->next());
810 bool LyXText::fullRebreak(BufferView * bview)
816 if (need_break_row) {
817 breakAgain(bview, need_break_row);
825 // important for the screen
828 /* the cursor set functions have a special mechanism. When they
829 * realize, that you left an empty paragraph, they will delete it.
830 * They also delete the corresponding row */
832 // need the selection cursor:
833 void LyXText::setSelection(BufferView * bview)
835 bool const lsel = selection.set();
837 if (!selection.set()) {
838 last_sel_cursor = selection.cursor;
839 selection.start = selection.cursor;
840 selection.end = selection.cursor;
845 // first the toggling area
846 if (cursor.y() < last_sel_cursor.y()
847 || (cursor.y() == last_sel_cursor.y()
848 && cursor.x() < last_sel_cursor.x())) {
849 toggle_end_cursor = last_sel_cursor;
850 toggle_cursor = cursor;
852 toggle_end_cursor = cursor;
853 toggle_cursor = last_sel_cursor;
856 last_sel_cursor = cursor;
858 // and now the whole selection
860 if (selection.cursor.par() == cursor.par())
861 if (selection.cursor.pos() < cursor.pos()) {
862 selection.end = cursor;
863 selection.start = selection.cursor;
865 selection.end = selection.cursor;
866 selection.start = cursor;
868 else if (selection.cursor.y() < cursor.y() ||
869 (selection.cursor.y() == cursor.y()
870 && selection.cursor.x() < cursor.x())) {
871 selection.end = cursor;
872 selection.start = selection.cursor;
875 selection.end = selection.cursor;
876 selection.start = cursor;
879 // a selection with no contents is not a selection
880 if (selection.start.par() == selection.end.par() &&
881 selection.start.pos() == selection.end.pos())
882 selection.set(false);
884 if (inset_owner && (selection.set() || lsel))
885 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
889 string const LyXText::selectionAsString(Buffer const * buffer) const
891 if (!selection.set()) return string();
894 // Special handling if the whole selection is within one paragraph
895 if (selection.start.par() == selection.end.par()) {
896 result += selection.start.par()->asString(buffer,
897 selection.start.pos(),
898 selection.end.pos());
902 // The selection spans more than one paragraph
904 // First paragraph in selection
905 result += selection.start.par()->asString(buffer,
906 selection.start.pos(),
907 selection.start.par()->size())
910 // The paragraphs in between (if any)
911 LyXCursor tmpcur(selection.start);
912 tmpcur.par(tmpcur.par()->next());
913 while (tmpcur.par() != selection.end.par()) {
914 result += tmpcur.par()->asString(buffer, 0,
915 tmpcur.par()->size()) +"\n\n";
916 tmpcur.par(tmpcur.par()->next());
919 // Last paragraph in selection
920 result += selection.end.par()->asString(buffer, 0,
921 selection.end.pos());
927 void LyXText::clearSelection(BufferView * /*bview*/) const
929 selection.set(false);
930 selection.mark(false);
931 selection.end = selection.start = cursor;
935 void LyXText::cursorHome(BufferView * bview) const
937 setCursor(bview, cursor.par(), cursor.row()->pos());
941 void LyXText::cursorEnd(BufferView * bview) const
943 if (!cursor.row()->next()
944 || cursor.row()->next()->par() != cursor.row()->par()) {
945 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
947 if (cursor.par()->size() &&
948 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
949 || cursor.par()->isNewline(rowLast(cursor.row())))) {
950 setCursor(bview, cursor.par(), rowLast(cursor.row()));
952 setCursor(bview,cursor.par(),
953 rowLast(cursor.row()) + 1);
959 void LyXText::cursorTop(BufferView * bview) const
961 while (cursor.par()->previous())
962 cursor.par(cursor.par()->previous());
963 setCursor(bview, cursor.par(), 0);
967 void LyXText::cursorBottom(BufferView * bview) const
969 while (cursor.par()->next())
970 cursor.par(cursor.par()->next());
971 setCursor(bview, cursor.par(), cursor.par()->size());
975 void LyXText::toggleFree(BufferView * bview,
976 LyXFont const & font, bool toggleall)
978 // If the mask is completely neutral, tell user
979 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
980 // Could only happen with user style
981 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
985 // Try implicit word selection
986 // If there is a change in the language the implicit word selection
988 LyXCursor resetCursor = cursor;
989 bool implicitSelection = (font.language() == ignore_language
990 && font.number() == LyXFont::IGNORE)
991 ? selectWordWhenUnderCursor(bview) : false;
994 setFont(bview, font, toggleall);
996 // Implicit selections are cleared afterwards
997 //and cursor is set to the original position.
998 if (implicitSelection) {
999 clearSelection(bview);
1000 cursor = resetCursor;
1001 setCursor(bview, cursor.par(), cursor.pos());
1002 selection.cursor = cursor;
1005 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1009 Paragraph::size_type
1010 LyXText::beginningOfMainBody(Buffer const * buf,
1011 Paragraph const * par) const
1013 if (textclasslist.Style(buf->params.textclass,
1014 par->getLayout()).labeltype != LABEL_MANUAL)
1017 return par->beginningOfMainBody();
1021 /* the DTP switches for paragraphs. LyX will store them in the
1022 * first physicla paragraph. When a paragraph is broken, the top settings
1023 * rest, the bottom settings are given to the new one. So I can make shure,
1024 * they do not duplicate themself and you cannnot make dirty things with
1027 void LyXText::setParagraph(BufferView * bview,
1028 bool line_top, bool line_bottom,
1029 bool pagebreak_top, bool pagebreak_bottom,
1030 VSpace const & space_top,
1031 VSpace const & space_bottom,
1033 string labelwidthstring,
1036 LyXCursor tmpcursor = cursor;
1037 if (!selection.set()) {
1038 selection.start = cursor;
1039 selection.end = cursor;
1042 // make sure that the depth behind the selection are restored, too
1043 Paragraph * endpar = selection.end.par()->next();
1044 Paragraph * undoendpar = endpar;
1046 if (endpar && endpar->getDepth()) {
1047 while (endpar && endpar->getDepth()) {
1048 endpar = endpar->next();
1049 undoendpar = endpar;
1053 // because of parindents etc.
1054 endpar = endpar->next();
1057 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1060 Paragraph * tmppar = selection.end.par();
1061 while (tmppar != selection.start.par()->previous()) {
1062 setCursor(bview, tmppar, 0);
1063 status(bview, LyXText::NEED_MORE_REFRESH);
1064 refresh_row = cursor.row();
1065 refresh_y = cursor.y() - cursor.row()->baseline();
1066 cursor.par()->params().lineTop(line_top);
1067 cursor.par()->params().lineBottom(line_bottom);
1068 cursor.par()->params().pagebreakTop(pagebreak_top);
1069 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1070 cursor.par()->params().spaceTop(space_top);
1071 cursor.par()->params().spaceBottom(space_bottom);
1072 // does the layout allow the new alignment?
1073 if (align == LYX_ALIGN_LAYOUT)
1074 align = textclasslist
1075 .Style(bview->buffer()->params.textclass,
1076 cursor.par()->getLayout()).align;
1077 if (align & textclasslist
1078 .Style(bview->buffer()->params.textclass,
1079 cursor.par()->getLayout()).alignpossible) {
1080 if (align == textclasslist
1081 .Style(bview->buffer()->params.textclass,
1082 cursor.par()->getLayout()).align)
1083 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1085 cursor.par()->params().align(align);
1087 cursor.par()->setLabelWidthString(labelwidthstring);
1088 cursor.par()->params().noindent(noindent);
1089 tmppar = cursor.par()->previous();
1092 redoParagraphs(bview, selection.start, endpar);
1094 clearSelection(bview);
1095 setCursor(bview, selection.start.par(), selection.start.pos());
1096 selection.cursor = cursor;
1097 setCursor(bview, selection.end.par(), selection.end.pos());
1098 setSelection(bview);
1099 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1101 bview->updateInset(inset_owner, true);
1105 char loweralphaCounter(int n)
1107 if (n < 1 || n > 26)
1117 char alphaCounter(int n)
1119 if (n < 1 || n > 26)
1127 char hebrewCounter(int n)
1129 static const char hebrew[22] = {
1130 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1131 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1134 if (n < 1 || n > 22)
1142 string const romanCounter(int n)
1144 static char const * roman[20] = {
1145 "i", "ii", "iii", "iv", "v",
1146 "vi", "vii", "viii", "ix", "x",
1147 "xi", "xii", "xiii", "xiv", "xv",
1148 "xvi", "xvii", "xviii", "xix", "xx"
1150 if (n < 1 || n > 20)
1159 // set the counter of a paragraph. This includes the labels
1160 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1162 LyXLayout const & layout =
1163 textclasslist.Style(buf->params.textclass,
1166 LyXTextClass const & textclass =
1167 textclasslist.TextClass(buf->params.textclass);
1169 // copy the prev-counters to this one,
1170 // unless this is the first paragraph
1171 if (par->previous()) {
1172 for (int i = 0; i < 10; ++i) {
1173 par->setCounter(i, par->previous()->getFirstCounter(i));
1175 par->params().appendix(par->previous()->params().appendix());
1176 if (!par->params().appendix() && par->params().startOfAppendix()) {
1177 par->params().appendix(true);
1178 for (int i = 0; i < 10; ++i) {
1179 par->setCounter(i, 0);
1182 par->enumdepth = par->previous()->enumdepth;
1183 par->itemdepth = par->previous()->itemdepth;
1185 for (int i = 0; i < 10; ++i) {
1186 par->setCounter(i, 0);
1188 par->params().appendix(par->params().startOfAppendix());
1193 /* Maybe we have to increment the enumeration depth.
1194 * BUT, enumeration in a footnote is considered in isolation from its
1195 * surrounding paragraph so don't increment if this is the
1196 * first line of the footnote
1197 * AND, bibliographies can't have their depth changed ie. they
1198 * are always of depth 0
1201 && par->previous()->getDepth() < par->getDepth()
1202 && textclasslist.Style(buf->params.textclass,
1203 par->previous()->getLayout()
1204 ).labeltype == LABEL_COUNTER_ENUMI
1205 && par->enumdepth < 3
1206 && layout.labeltype != LABEL_BIBLIO) {
1210 // Maybe we have to decrement the enumeration depth, see note above
1212 && par->previous()->getDepth() > par->getDepth()
1213 && layout.labeltype != LABEL_BIBLIO) {
1214 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1215 par->setCounter(6 + par->enumdepth,
1216 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1217 /* reset the counters.
1218 * A depth change is like a breaking layout
1220 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1221 par->setCounter(i, 0);
1224 if (!par->params().labelString().empty()) {
1225 par->params().labelString(string());
1228 if (layout.margintype == MARGIN_MANUAL) {
1229 if (par->params().labelWidthString().empty()) {
1230 par->setLabelWidthString(layout.labelstring());
1233 par->setLabelWidthString(string());
1236 // is it a layout that has an automatic label?
1237 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1239 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1240 if (i >= 0 && i<= buf->params.secnumdepth) {
1241 par->incCounter(i); // increment the counter
1243 // Is there a label? Useful for Chapter layout
1244 if (!par->params().appendix()) {
1245 if (!layout.labelstring().empty())
1246 par->params().labelString(layout.labelstring());
1248 par->params().labelString(string());
1250 if (!layout.labelstring_appendix().empty())
1251 par->params().labelString(layout.labelstring_appendix());
1253 par->params().labelString(string());
1256 std::ostringstream s;
1258 if (!par->params().appendix()) {
1259 switch (2 * LABEL_COUNTER_CHAPTER -
1260 textclass.maxcounter() + i) {
1261 case LABEL_COUNTER_CHAPTER:
1262 s << par->getCounter(i);
1264 case LABEL_COUNTER_SECTION:
1265 s << par->getCounter(i - 1) << '.'
1266 << par->getCounter(i);
1268 case LABEL_COUNTER_SUBSECTION:
1269 s << par->getCounter(i - 2) << '.'
1270 << par->getCounter(i - 1) << '.'
1271 << par->getCounter(i);
1273 case LABEL_COUNTER_SUBSUBSECTION:
1274 s << par->getCounter(i - 3) << '.'
1275 << par->getCounter(i - 2) << '.'
1276 << par->getCounter(i - 1) << '.'
1277 << par->getCounter(i);
1280 case LABEL_COUNTER_PARAGRAPH:
1281 s << par->getCounter(i - 4) << '.'
1282 << par->getCounter(i - 3) << '.'
1283 << par->getCounter(i - 2) << '.'
1284 << par->getCounter(i - 1) << '.'
1285 << par->getCounter(i);
1287 case LABEL_COUNTER_SUBPARAGRAPH:
1288 s << par->getCounter(i - 5) << '.'
1289 << par->getCounter(i - 4) << '.'
1290 << par->getCounter(i - 3) << '.'
1291 << par->getCounter(i - 2) << '.'
1292 << par->getCounter(i - 1) << '.'
1293 << par->getCounter(i);
1297 // Can this ever be reached? And in the
1298 // case it is, how can this be correct?
1300 s << par->getCounter(i) << '.';
1303 } else { // appendix
1304 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1305 case LABEL_COUNTER_CHAPTER:
1306 if (par->isRightToLeftPar(buf->params))
1307 s << hebrewCounter(par->getCounter(i));
1309 s << alphaCounter(par->getCounter(i));
1311 case LABEL_COUNTER_SECTION:
1312 if (par->isRightToLeftPar(buf->params))
1313 s << hebrewCounter(par->getCounter(i - 1));
1315 s << alphaCounter(par->getCounter(i - 1));
1318 << par->getCounter(i);
1321 case LABEL_COUNTER_SUBSECTION:
1322 if (par->isRightToLeftPar(buf->params))
1323 s << hebrewCounter(par->getCounter(i - 2));
1325 s << alphaCounter(par->getCounter(i - 2));
1328 << par->getCounter(i-1) << '.'
1329 << par->getCounter(i);
1332 case LABEL_COUNTER_SUBSUBSECTION:
1333 if (par->isRightToLeftPar(buf->params))
1334 s << hebrewCounter(par->getCounter(i-3));
1336 s << alphaCounter(par->getCounter(i-3));
1339 << par->getCounter(i-2) << '.'
1340 << par->getCounter(i-1) << '.'
1341 << par->getCounter(i);
1344 case LABEL_COUNTER_PARAGRAPH:
1345 if (par->isRightToLeftPar(buf->params))
1346 s << hebrewCounter(par->getCounter(i-4));
1348 s << alphaCounter(par->getCounter(i-4));
1351 << par->getCounter(i-3) << '.'
1352 << par->getCounter(i-2) << '.'
1353 << par->getCounter(i-1) << '.'
1354 << par->getCounter(i);
1357 case LABEL_COUNTER_SUBPARAGRAPH:
1358 if (par->isRightToLeftPar(buf->params))
1359 s << hebrewCounter(par->getCounter(i-5));
1361 s << alphaCounter(par->getCounter(i-5));
1364 << par->getCounter(i-4) << '.'
1365 << par->getCounter(i-3) << '.'
1366 << par->getCounter(i-2) << '.'
1367 << par->getCounter(i-1) << '.'
1368 << par->getCounter(i);
1372 // Can this ever be reached? And in the
1373 // case it is, how can this be correct?
1375 s << par->getCounter(i) << '.';
1381 par->params().labelString(par->params().labelString() +s.str().c_str());
1382 // We really want to remove the c_str as soon as
1385 for (i++; i < 10; ++i) {
1386 // reset the following counters
1387 par->setCounter(i, 0);
1389 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1390 for (i++; i < 10; ++i) {
1391 // reset the following counters
1392 par->setCounter(i, 0);
1394 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1395 par->incCounter(i + par->enumdepth);
1396 int number = par->getCounter(i + par->enumdepth);
1398 std::ostringstream s;
1400 switch (par->enumdepth) {
1402 if (par->isRightToLeftPar(buf->params))
1404 << hebrewCounter(number)
1408 << loweralphaCounter(number)
1412 if (par->isRightToLeftPar(buf->params))
1413 s << '.' << romanCounter(number);
1415 s << romanCounter(number) << '.';
1418 if (par->isRightToLeftPar(buf->params))
1420 << alphaCounter(number);
1422 s << alphaCounter(number)
1426 if (par->isRightToLeftPar(buf->params))
1433 par->params().labelString(s.str().c_str());
1435 for (i += par->enumdepth + 1; i < 10; ++i) {
1436 // reset the following counters
1437 par->setCounter(i, 0);
1441 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1442 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1444 int number = par->getCounter(i);
1446 InsetCommandParams p( "bibitem" );
1447 par->bibkey = new InsetBibKey(p);
1449 par->bibkey->setCounter(number);
1450 par->params().labelString(layout.labelstring());
1452 // In biblio should't be following counters but...
1454 string s = layout.labelstring();
1456 // the caption hack:
1457 if (layout.labeltype == LABEL_SENSITIVE) {
1458 bool isOK (par->InInset() && par->InInset()->owner() &&
1459 (par->InInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1462 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1464 = floatList.getType(tmp->type());
1465 // We should get the correct number here too.
1466 s = fl.name() + " #:";
1468 /* par->SetLayout(0);
1469 s = layout->labelstring; */
1470 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1471 ? " :úåòîùî øñç" : "Senseless: ";
1474 par->params().labelString(s);
1476 /* reset the enumeration counter. They are always resetted
1477 * when there is any other layout between */
1478 for (int i = 6 + par->enumdepth; i < 10; ++i)
1479 par->setCounter(i, 0);
1484 // Updates all counters BEHIND the row. Changed paragraphs
1485 // with a dynamic left margin will be rebroken.
1486 void LyXText::updateCounters(BufferView * bview, Row * row) const
1494 par = row->par()->next();
1498 while (row->par() != par)
1501 setCounter(bview->buffer(), par);
1503 // now check for the headline layouts. remember that they
1504 // have a dynamic left margin
1505 if ((textclasslist.Style(bview->buffer()->params.textclass,
1506 par->layout).margintype == MARGIN_DYNAMIC
1507 || textclasslist.Style(bview->buffer()->params.textclass,
1508 par->layout).labeltype == LABEL_SENSITIVE)) {
1510 // Rebreak the paragraph
1511 removeParagraph(row);
1512 appendParagraph(bview, row);
1519 void LyXText::insertInset(BufferView * bview, Inset * inset)
1521 if (!cursor.par()->insertInsetAllowed(inset))
1523 setUndo(bview, Undo::INSERT,
1524 cursor.par(), cursor.par()->next());
1525 cursor.par()->insertInset(cursor.pos(), inset);
1526 // Just to rebreak and refresh correctly.
1527 // The character will not be inserted a second time
1528 insertChar(bview, Paragraph::META_INSET);
1530 // If we enter a highly editable inset the cursor should be to before
1531 // the inset. This couldn't happen before as Undo was not handled inside
1532 // inset now after the Undo LyX tries to call inset->Edit(...) again
1533 // and cannot do this as the cursor is behind the inset and GetInset
1534 // does not return the inset!
1535 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1536 cursorLeft(bview, true);
1542 void LyXText::copyEnvironmentType()
1544 copylayouttype = cursor.par()->getLayout();
1548 void LyXText::pasteEnvironmentType(BufferView * bview)
1550 setLayout(bview, copylayouttype);
1554 void LyXText::cutSelection(BufferView * bview, bool doclear)
1556 // Stuff what we got on the clipboard. Even if there is no selection.
1558 // There is a problem with having the stuffing here in that the
1559 // larger the selection the slower LyX will get. This can be
1560 // solved by running the line below only when the selection has
1561 // finished. The solution used currently just works, to make it
1562 // faster we need to be more clever and probably also have more
1563 // calls to stuffClipboard. (Lgb)
1564 bview->stuffClipboard(selectionAsString(bview->buffer()));
1566 // This doesn't make sense, if there is no selection
1567 if (!selection.set())
1570 // OK, we have a selection. This is always between selection.start
1571 // and selection.end
1573 // make sure that the depth behind the selection are restored, too
1574 Paragraph * endpar = selection.end.par()->next();
1575 Paragraph * undoendpar = endpar;
1577 if (endpar && endpar->getDepth()) {
1578 while (endpar && endpar->getDepth()) {
1579 endpar = endpar->next();
1580 undoendpar = endpar;
1582 } else if (endpar) {
1583 endpar = endpar->next(); // because of parindents etc.
1586 setUndo(bview, Undo::DELETE,
1587 selection.start.par(), undoendpar);
1589 // there are two cases: cut only within one paragraph or
1590 // more than one paragraph
1591 if (selection.start.par() == selection.end.par()) {
1592 // only within one paragraph
1593 endpar = selection.end.par();
1594 int pos = selection.end.pos();
1595 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1596 selection.start.pos(), pos,
1597 bview->buffer()->params.textclass, doclear);
1598 selection.end.pos(pos);
1600 endpar = selection.end.par();
1601 int pos = selection.end.pos();
1602 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1603 selection.start.pos(), pos,
1604 bview->buffer()->params.textclass, doclear);
1606 selection.end.par(endpar);
1607 selection.end.pos(pos);
1608 cursor.pos(selection.end.pos());
1610 endpar = endpar->next();
1612 // sometimes necessary
1614 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1616 redoParagraphs(bview, selection.start, endpar);
1618 // cutSelection can invalidate the cursor so we need to set
1620 cursor = selection.start;
1622 // need a valid cursor. (Lgb)
1623 clearSelection(bview);
1625 setCursor(bview, cursor.par(), cursor.pos());
1626 selection.cursor = cursor;
1627 updateCounters(bview, cursor.row());
1631 void LyXText::copySelection(BufferView * bview)
1633 // Stuff what we got on the clipboard. Even if there is no selection.
1635 // There is a problem with having the stuffing here in that the
1636 // larger the selection the slower LyX will get. This can be
1637 // solved by running the line below only when the selection has
1638 // finished. The solution used currently just works, to make it
1639 // faster we need to be more clever and probably also have more
1640 // calls to stuffClipboard. (Lgb)
1641 bview->stuffClipboard(selectionAsString(bview->buffer()));
1643 // this doesnt make sense, if there is no selection
1644 if (!selection.set())
1647 // ok we have a selection. This is always between selection.start
1648 // and sel_end cursor
1650 // copy behind a space if there is one
1651 while (selection.start.par()->size() > selection.start.pos()
1652 && selection.start.par()->isLineSeparator(selection.start.pos())
1653 && (selection.start.par() != selection.end.par()
1654 || selection.start.pos() < selection.end.pos()))
1655 selection.start.pos(selection.start.pos() + 1);
1657 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1658 selection.start.pos(), selection.end.pos(),
1659 bview->buffer()->params.textclass);
1663 void LyXText::pasteSelection(BufferView * bview)
1665 // this does not make sense, if there is nothing to paste
1666 if (!CutAndPaste::checkPastePossible(cursor.par()))
1669 setUndo(bview, Undo::INSERT,
1670 cursor.par(), cursor.par()->next());
1673 Paragraph * actpar = cursor.par();
1675 int pos = cursor.pos();
1676 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1677 bview->buffer()->params.textclass);
1679 redoParagraphs(bview, cursor, endpar);
1681 setCursor(bview, cursor.par(), cursor.pos());
1682 clearSelection(bview);
1684 selection.cursor = cursor;
1685 setCursor(bview, actpar, pos);
1686 setSelection(bview);
1687 updateCounters(bview, cursor.row());
1691 // returns a pointer to the very first Paragraph
1692 Paragraph * LyXText::firstParagraph() const
1694 return ownerParagraph();
1698 // sets the selection over the number of characters of string, no check!!
1699 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1704 selection.cursor = cursor;
1705 for (string::size_type i = 0; i < str.length(); ++i)
1707 setSelection(bview);
1711 // simple replacing. The font of the first selected character is used
1712 void LyXText::replaceSelectionWithString(BufferView * bview,
1715 setCursorParUndo(bview);
1718 if (!selection.set()) { // create a dummy selection
1719 selection.end = cursor;
1720 selection.start = cursor;
1723 // Get font setting before we cut
1724 Paragraph::size_type pos = selection.end.pos();
1725 LyXFont const font = selection.start.par()
1726 ->getFontSettings(bview->buffer()->params,
1727 selection.start.pos());
1729 // Insert the new string
1730 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1731 selection.end.par()->insertChar(pos, (*cit), font);
1735 // Cut the selection
1736 cutSelection(bview);
1742 // needed to insert the selection
1743 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1745 Paragraph * par = cursor.par();
1746 Paragraph::size_type pos = cursor.pos();
1747 Paragraph * endpar = cursor.par()->next();
1749 setCursorParUndo(bview);
1751 bool isEnvironment =
1752 textclasslist.Style(bview->buffer()->params.textclass,
1753 cursor.par()->getLayout()).isEnvironment();
1755 textclasslist.Style(bview->buffer()->params.textclass,
1756 cursor.par()->getLayout()).free_spacing;
1758 textclasslist.Style(bview->buffer()->params.textclass,
1759 cursor.par()->getLayout()).keepempty;
1761 // only to be sure, should not be neccessary
1762 clearSelection(bview);
1764 // insert the string, don't insert doublespace
1765 bool space_inserted = true;
1766 for(string::const_iterator cit = str.begin();
1767 cit != str.end(); ++cit) {
1769 if (par->size() || keepempty) {
1770 par->breakParagraph(bview->buffer()->params,
1771 pos, isEnvironment);
1774 space_inserted = true;
1778 // do not insert consecutive spaces if !free_spacing
1779 } else if ((*cit == ' ' || *cit == '\t')
1780 && space_inserted && !free_spacing) {
1782 } else if (*cit == '\t') {
1783 if (!free_spacing) {
1784 // tabs are like spaces here
1785 par->insertChar(pos, ' ',
1788 space_inserted = true;
1790 const Paragraph::value_type nb = 8 - pos % 8;
1791 for (Paragraph::size_type a = 0;
1793 par->insertChar(pos, ' ',
1797 space_inserted = true;
1799 } else if (!IsPrintable(*cit)) {
1800 // Ignore unprintables
1803 // just insert the character
1804 par->insertChar(pos, *cit, current_font);
1806 space_inserted = (*cit == ' ');
1811 redoParagraphs(bview, cursor, endpar);
1812 setCursor(bview, cursor.par(), cursor.pos());
1813 selection.cursor = cursor;
1814 setCursor(bview, par, pos);
1815 setSelection(bview);
1819 // turns double-CR to single CR, others where converted into one
1820 // blank. Then InsertStringAsLines is called
1821 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1823 string linestr(str);
1824 bool newline_inserted = false;
1825 for (string::size_type i = 0; i < linestr.length(); ++i) {
1826 if (linestr[i] == '\n') {
1827 if (newline_inserted) {
1828 // we know that \r will be ignored by
1829 // InsertStringA. Of course, it is a dirty
1830 // trick, but it works...
1831 linestr[i - 1] = '\r';
1835 newline_inserted = true;
1837 } else if (IsPrintable(linestr[i])) {
1838 newline_inserted = false;
1841 insertStringAsLines(bview, linestr);
1845 bool LyXText::gotoNextInset(BufferView * bview,
1846 std::vector<Inset::Code> const & codes,
1847 string const & contents) const
1849 LyXCursor res = cursor;
1852 if (res.pos() < res.par()->size() - 1) {
1853 res.pos(res.pos() + 1);
1855 res.par(res.par()->next());
1859 } while (res.par() &&
1860 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1861 && (inset = res.par()->getInset(res.pos())) != 0
1862 && find(codes.begin(), codes.end(), inset->lyxCode())
1864 && (contents.empty() ||
1865 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1869 setCursor(bview, res.par(), res.pos());
1876 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1877 Paragraph::size_type pos)
1879 LyXCursor tmpcursor;
1882 Paragraph::size_type z;
1883 Row * row = getRow(par, pos, y);
1885 // is there a break one row above
1886 if (row->previous() && row->previous()->par() == row->par()) {
1887 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1888 if (z >= row->pos()) {
1889 // set the dimensions of the row above
1890 y -= row->previous()->height();
1892 refresh_row = row->previous();
1893 status(bview, LyXText::NEED_MORE_REFRESH);
1895 breakAgain(bview, row->previous());
1897 // set the cursor again. Otherwise
1898 // dangling pointers are possible
1899 setCursor(bview, cursor.par(), cursor.pos(),
1900 false, cursor.boundary());
1901 selection.cursor = cursor;
1906 int const tmpheight = row->height();
1907 Paragraph::size_type const tmplast = rowLast(row);
1911 breakAgain(bview, row);
1912 if (row->height() == tmpheight && rowLast(row) == tmplast)
1913 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1915 status(bview, LyXText::NEED_MORE_REFRESH);
1917 // check the special right address boxes
1918 if (textclasslist.Style(bview->buffer()->params.textclass,
1919 par->getLayout()).margintype
1920 == MARGIN_RIGHT_ADDRESS_BOX) {
1927 redoDrawingOfParagraph(bview, tmpcursor);
1930 // set the cursor again. Otherwise dangling pointers are possible
1931 // also set the selection
1933 if (selection.set()) {
1935 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1936 false, selection.cursor.boundary());
1937 selection.cursor = cursor;
1938 setCursorIntern(bview, selection.start.par(),
1939 selection.start.pos(),
1940 false, selection.start.boundary());
1941 selection.start = cursor;
1942 setCursorIntern(bview, selection.end.par(),
1943 selection.end.pos(),
1944 false, selection.end.boundary());
1945 selection.end = cursor;
1946 setCursorIntern(bview, last_sel_cursor.par(),
1947 last_sel_cursor.pos(),
1948 false, last_sel_cursor.boundary());
1949 last_sel_cursor = cursor;
1952 setCursorIntern(bview, cursor.par(), cursor.pos(),
1953 false, cursor.boundary());
1957 // returns false if inset wasn't found
1958 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1960 // first check the current paragraph
1961 int pos = cursor.par()->getPositionOfInset(inset);
1963 checkParagraph(bview, cursor.par(), pos);
1967 // check every paragraph
1969 Paragraph * par = firstParagraph();
1971 pos = par->getPositionOfInset(inset);
1973 checkParagraph(bview, par, pos);
1983 void LyXText::setCursor(BufferView * bview, Paragraph * par,
1984 Paragraph::size_type pos,
1985 bool setfont, bool boundary) const
1987 LyXCursor old_cursor = cursor;
1988 setCursorIntern(bview, par, pos, setfont, boundary);
1989 deleteEmptyParagraphMechanism(bview, old_cursor);
1993 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
1994 Paragraph::size_type pos, bool boundary) const
1998 cur.boundary(boundary);
2000 // get the cursor y position in text
2002 Row * row = getRow(par, pos, y);
2003 // y is now the beginning of the cursor row
2004 y += row->baseline();
2005 // y is now the cursor baseline
2008 // now get the cursors x position
2010 float fill_separator;
2012 float fill_label_hfill;
2013 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2015 Paragraph::size_type cursor_vpos = 0;
2016 Paragraph::size_type last = rowLastPrintable(row);
2018 if (pos > last + 1) // This shouldn't happen.
2020 else if (pos < row->pos())
2023 if (last < row->pos())
2024 cursor_vpos = row->pos();
2025 else if (pos > last && !boundary)
2026 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2027 ? row->pos() : last + 1;
2028 else if (pos > row->pos() &&
2029 (pos > last || boundary))
2030 /// Place cursor after char at (logical) position pos - 1
2031 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2032 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2034 /// Place cursor before char at (logical) position pos
2035 cursor_vpos = (bidi_level(pos) % 2 == 0)
2036 ? log2vis(pos) : log2vis(pos) + 1;
2038 Paragraph::size_type main_body =
2039 beginningOfMainBody(bview->buffer(), row->par());
2040 if ((main_body > 0) &&
2041 ((main_body-1 > last) ||
2042 !row->par()->isLineSeparator(main_body-1)))
2045 for (Paragraph::size_type vpos = row->pos();
2046 vpos < cursor_vpos; ++vpos) {
2047 pos = vis2log(vpos);
2048 if (main_body > 0 && pos == main_body - 1) {
2049 x += fill_label_hfill +
2050 lyxfont::width(textclasslist.Style(
2051 bview->buffer()->params.textclass,
2052 row->par()->getLayout())
2054 getFont(bview->buffer(), row->par(), -2));
2055 if (row->par()->isLineSeparator(main_body-1))
2056 x -= singleWidth(bview, row->par(),main_body-1);
2058 if (hfillExpansion(bview->buffer(), row, pos)) {
2059 x += singleWidth(bview, row->par(), pos);
2060 if (pos >= main_body)
2063 x += fill_label_hfill;
2064 } else if (row->par()->isSeparator(pos)) {
2065 x += singleWidth(bview, row->par(), pos);
2066 if (pos >= main_body)
2067 x += fill_separator;
2069 x += singleWidth(bview, row->par(), pos);
2078 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2079 Paragraph::size_type pos,
2080 bool setfont, bool boundary) const
2082 InsetText * it = static_cast<InsetText *>(par->InInset());
2083 if (it && (it != inset_owner)) {
2084 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2087 setCursor(bview, cursor, par, pos, boundary);
2089 setCurrentFont(bview);
2094 void LyXText::setCurrentFont(BufferView * bview) const
2096 Paragraph::size_type pos = cursor.pos();
2097 if (cursor.boundary() && pos > 0)
2101 if (pos == cursor.par()->size())
2103 else // potentional bug... BUG (Lgb)
2104 if (cursor.par()->isSeparator(pos)) {
2105 if (pos > cursor.row()->pos() &&
2106 bidi_level(pos) % 2 ==
2107 bidi_level(pos - 1) % 2)
2109 else if (pos + 1 < cursor.par()->size())
2115 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2116 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2118 if (cursor.pos() == cursor.par()->size() &&
2119 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2120 !cursor.boundary()) {
2121 Language const * lang =
2122 cursor.par()->getParLanguage(bview->buffer()->params);
2123 current_font.setLanguage(lang);
2124 current_font.setNumber(LyXFont::OFF);
2125 real_current_font.setLanguage(lang);
2126 real_current_font.setNumber(LyXFont::OFF);
2131 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2133 LyXCursor old_cursor = cursor;
2135 // Get the row first.
2137 Row * row = getRowNearY(y);
2138 cursor.par(row->par());
2141 int column = getColumnNearX(bview, row, x, bound);
2142 cursor.pos(row->pos() + column);
2144 cursor.y(y + row->baseline());
2146 cursor.boundary(bound);
2147 setCurrentFont(bview);
2148 deleteEmptyParagraphMechanism(bview, old_cursor);
2152 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2155 // Get the row first.
2157 Row * row = getRowNearY(y);
2159 int column = getColumnNearX(bview, row, x, bound);
2161 cur.par(row->par());
2162 cur.pos(row->pos() + column);
2164 cur.y(y + row->baseline());
2166 cur.boundary(bound);
2170 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2172 if (cursor.pos() > 0) {
2173 bool boundary = cursor.boundary();
2174 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2175 if (!internal && !boundary &&
2176 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2177 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2178 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2179 Paragraph * par = cursor.par()->previous();
2180 setCursor(bview, par, par->size());
2185 void LyXText::cursorRight(BufferView * bview, bool internal) const
2187 if (!internal && cursor.boundary() &&
2188 !cursor.par()->isNewline(cursor.pos()))
2189 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2190 else if (cursor.pos() < cursor.par()->size()) {
2191 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2193 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2194 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2195 } else if (cursor.par()->next())
2196 setCursor(bview, cursor.par()->next(), 0);
2200 void LyXText::cursorUp(BufferView * bview) const
2202 setCursorFromCoordinates(bview, cursor.x_fix(),
2203 cursor.y() - cursor.row()->baseline() - 1);
2207 void LyXText::cursorDown(BufferView * bview) const
2209 setCursorFromCoordinates(bview, cursor.x_fix(),
2210 cursor.y() - cursor.row()->baseline()
2211 + cursor.row()->height() + 1);
2215 void LyXText::cursorUpParagraph(BufferView * bview) const
2217 if (cursor.pos() > 0) {
2218 setCursor(bview, cursor.par(), 0);
2220 else if (cursor.par()->previous()) {
2221 setCursor(bview, cursor.par()->previous(), 0);
2226 void LyXText::cursorDownParagraph(BufferView * bview) const
2228 if (cursor.par()->next()) {
2229 setCursor(bview, cursor.par()->next(), 0);
2231 setCursor(bview, cursor.par(), cursor.par()->size());
2236 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2237 LyXCursor const & old_cursor) const
2239 // Would be wrong to delete anything if we have a selection.
2240 if (selection.set()) return;
2242 // We allow all kinds of "mumbo-jumbo" when freespacing.
2243 if (textclasslist.Style(bview->buffer()->params.textclass,
2244 old_cursor.par()->getLayout()).free_spacing)
2247 bool deleted = false;
2249 /* Ok I'll put some comments here about what is missing.
2250 I have fixed BackSpace (and thus Delete) to not delete
2251 double-spaces automagically. I have also changed Cut,
2252 Copy and Paste to hopefully do some sensible things.
2253 There are still some small problems that can lead to
2254 double spaces stored in the document file or space at
2255 the beginning of paragraphs. This happens if you have
2256 the cursor betwenn to spaces and then save. Or if you
2257 cut and paste and the selection have a space at the
2258 beginning and then save right after the paste. I am
2259 sure none of these are very hard to fix, but I will
2260 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2261 that I can get some feedback. (Lgb)
2264 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2265 // delete the LineSeparator.
2268 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2269 // delete the LineSeparator.
2272 // If the pos around the old_cursor were spaces, delete one of them.
2273 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2274 // Only if the cursor has really moved
2276 if (old_cursor.pos() > 0
2277 && old_cursor.pos() < old_cursor.par()->size()
2278 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2279 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2280 old_cursor.par()->erase(old_cursor.pos() - 1);
2281 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2283 if (old_cursor.par() == cursor.par() &&
2284 cursor.pos() > old_cursor.pos()) {
2285 setCursorIntern(bview, cursor.par(),
2288 setCursorIntern(bview, cursor.par(),
2294 // Do not delete empty paragraphs with keepempty set.
2295 if ((textclasslist.Style(bview->buffer()->params.textclass,
2296 old_cursor.par()->getLayout())).keepempty)
2299 LyXCursor tmpcursor;
2301 if (old_cursor.par() != cursor.par()) {
2302 if ((old_cursor.par()->size() == 0
2303 || (old_cursor.par()->size() == 1
2304 && old_cursor.par()->isLineSeparator(0)))) {
2305 // ok, we will delete anything
2307 // make sure that you do not delete any environments
2308 status(bview, LyXText::NEED_MORE_REFRESH);
2311 if (old_cursor.row()->previous()) {
2312 refresh_row = old_cursor.row()->previous();
2313 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2315 cursor = old_cursor; // that undo can restore the right cursor position
2316 Paragraph * endpar = old_cursor.par()->next();
2317 if (endpar && endpar->getDepth()) {
2318 while (endpar && endpar->getDepth()) {
2319 endpar = endpar->next();
2322 setUndo(bview, Undo::DELETE,
2328 removeRow(old_cursor.row());
2329 if (ownerParagraph() == old_cursor.par()) {
2330 ownerParagraph(ownerParagraph()->next());
2333 delete old_cursor.par();
2335 /* Breakagain the next par. Needed
2336 * because of the parindent that
2337 * can occur or dissappear. The
2338 * next row can change its height,
2339 * if there is another layout before */
2340 if (refresh_row->next()) {
2341 breakAgain(bview, refresh_row->next());
2342 updateCounters(bview, refresh_row);
2344 setHeightOfRow(bview, refresh_row);
2346 refresh_row = old_cursor.row()->next();
2347 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2350 cursor = old_cursor; // that undo can restore the right cursor position
2351 Paragraph * endpar = old_cursor.par()->next();
2352 if (endpar && endpar->getDepth()) {
2353 while (endpar && endpar->getDepth()) {
2354 endpar = endpar->next();
2357 setUndo(bview, Undo::DELETE,
2363 removeRow(old_cursor.row());
2365 if (ownerParagraph() == old_cursor.par()) {
2366 ownerParagraph(ownerParagraph()->next());
2369 delete old_cursor.par();
2371 /* Breakagain the next par. Needed
2372 because of the parindent that can
2373 occur or dissappear.
2374 The next row can change its height,
2375 if there is another layout before
2378 breakAgain(bview, refresh_row);
2379 updateCounters(bview, refresh_row->previous());
2385 setCursorIntern(bview, cursor.par(), cursor.pos());
2387 if (selection.cursor.par() == old_cursor.par()
2388 && selection.cursor.pos() == selection.cursor.pos()) {
2389 // correct selection
2390 selection.cursor = cursor;
2394 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2395 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2397 setCursorIntern(bview, cursor.par(), cursor.pos());
2398 selection.cursor = cursor;
2405 void LyXText::toggleAppendix(BufferView * bview)
2407 Paragraph * par = cursor.par();
2408 bool start = !par->params().startOfAppendix();
2410 // ensure that we have only one start_of_appendix in this document
2411 Paragraph * tmp = firstParagraph();
2412 for (; tmp; tmp = tmp->next()) {
2413 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;
2446 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2448 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2449 if (op && op->InInset()) {
2450 static_cast<InsetText *>(op->InInset())->paragraph(p);
2453 inset_owner->paragraph(p);
2455 bv_owner->buffer()->paragraph = p;
2462 LyXText::text_status LyXText::status() const
2468 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2471 if ((status_ != NEED_MORE_REFRESH)
2472 || (status_ == NEED_MORE_REFRESH)
2473 && (st != NEED_VERY_LITTLE_REFRESH)) {
2475 if (inset_owner && st != UNCHANGED) {
2476 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2480 #warning Please tell what the intention is here.
2481 // The above does not make any sense, I changed it to what is here,
2482 // but it still does not make much sense. (Lgb)
2483 if ((status_ != NEED_MORE_REFRESH)
2484 || (status_ == NEED_MORE_REFRESH
2485 && st != NEED_VERY_LITTLE_REFRESH)) {
2487 if (inset_owner && st != UNCHANGED) {
2488 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);