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 "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
41 #include "FloatList.h"
43 #include "ParagraphParameters.h"
44 #include "support/LAssert.h"
53 LyXText::LyXText(BufferView * bv)
54 : number_of_rows(0), height(0), width(0), first(0),
55 bv_owner(bv), inset_owner(0), the_locking_inset(0),
56 need_break_row(0), refresh_y(0), refresh_row(0),
57 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
62 LyXText::LyXText(InsetText * inset)
63 : number_of_rows(0), height(0), width(0), first(0),
64 bv_owner(0), inset_owner(inset), the_locking_inset(0),
65 need_break_row(0), refresh_y(0), refresh_row(0),
66 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
70 void LyXText::init(BufferView * bview, bool reinit)
73 // Delete all rows, this does not touch the paragraphs!
74 Row * tmprow = firstrow;
76 tmprow = firstrow->next();
80 lastrow = refresh_row = need_break_row = 0;
81 width = height = copylayouttype = 0;
82 number_of_rows = first = refresh_y = 0;
83 status_ = LyXText::UNCHANGED;
87 Paragraph * par = ownerParagraph();
88 current_font = getFont(bview->buffer(), par, 0);
90 insertParagraph(bview, par, lastrow);
93 setCursorIntern(bview, firstrow->par(), 0);
94 selection.cursor = cursor;
100 // Delete all rows, this does not touch the paragraphs!
101 Row * tmprow = firstrow;
103 tmprow = firstrow->next();
112 LyXFont const realizeFont(LyXFont const & font,
116 LyXFont tmpfont(font);
117 Paragraph::depth_type par_depth = par->getDepth();
119 // Resolve against environment font information
120 while (par && par_depth && !tmpfont.resolved()) {
121 par = par->outerHook();
123 #ifndef INHERIT_LANGUAGE
124 tmpfont.realize(textclasslist.
125 Style(buf->params.textclass,
126 par->getLayout()).font);
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font,
131 buf->params.language);
133 par_depth = par->getDepth();
137 #ifndef INHERIT_LANGUAGE
138 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
140 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
141 buf->params.language);
150 // Gets the fully instantiated font at a given position in a paragraph
151 // Basically the same routine as Paragraph::getFont() in paragraph.C.
152 // The difference is that this one is used for displaying, and thus we
153 // are allowed to make cosmetic improvements. For instance make footnotes
155 // If position is -1, we get the layout font of the paragraph.
156 // If position is -2, we get the font of the manual label of the paragraph.
157 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
158 Paragraph::size_type pos) const
160 lyx::Assert(pos >= 0);
162 LyXLayout const & layout =
163 textclasslist.Style(buf->params.textclass, par->getLayout());
165 Paragraph::depth_type par_depth = par->getDepth();
166 // We specialize the 95% common case:
168 if (layout.labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params,
173 #ifndef INHERIT_LANGUAGE
174 return f.realize(layout.reslabelfont);
176 return f.realize(layout.reslabelfont, buf->params.language);
179 LyXFont f = par->getFontSettings(buf->params, pos);
180 #ifndef INHERIT_LANGUAGE
181 return f.realize(layout.resfont);
183 return f.realize(layout.resfont, buf->params.language);
188 // The uncommon case need not be optimized as much
192 if (pos < beginningOfMainBody(buf, par)) {
194 layoutfont = layout.labelfont;
197 layoutfont = layout.font;
200 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
201 #ifndef INHERIT_LANGUAGE
202 tmpfont.realize(layoutfont);
204 tmpfont.realize(layoutfont, buf->params.language);
207 return realizeFont(tmpfont, buf, par);
211 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
213 LyXLayout const & layout =
214 textclasslist.Style(buf->params.textclass, par->getLayout());
216 Paragraph::depth_type par_depth = par->getDepth();
219 return layout.resfont;
222 return realizeFont(layout.font, buf, par);
226 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
228 LyXLayout const & layout =
229 textclasslist.Style(buf->params.textclass, par->getLayout());
231 Paragraph::depth_type par_depth = par->getDepth();
234 return layout.reslabelfont;
237 return realizeFont(layout.labelfont, buf, par);
241 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
242 Paragraph::size_type pos, LyXFont const & fnt,
245 Buffer const * buf = bv->buffer();
246 LyXFont font = getFont(buf, par, pos);
247 font.update(fnt, buf->params.language, toggleall);
248 // Let the insets convert their font
249 if (par->getChar(pos) == Paragraph::META_INSET) {
250 Inset * inset = par->getInset(pos);
252 if (inset->editable()==Inset::IS_EDITABLE) {
253 UpdatableInset * uinset =
254 static_cast<UpdatableInset *>(inset);
255 uinset->setFont(bv, fnt, toggleall, true);
260 LyXLayout const & layout =
261 textclasslist.Style(buf->params.textclass,
264 // Get concrete layout font to reduce against
267 if (pos < beginningOfMainBody(buf, par))
268 layoutfont = layout.labelfont;
270 layoutfont = layout.font;
272 // Realize against environment font information
273 if (par->getDepth()){
274 Paragraph * tp = par;
275 while (!layoutfont.resolved() && tp && tp->getDepth()) {
276 tp = tp->outerHook();
278 #ifndef INHERIT_LANGUAGE
279 layoutfont.realize(textclasslist.
280 Style(buf->params.textclass,
281 tp->getLayout()).font);
283 layoutfont.realize(textclasslist.
284 Style(buf->params.textclass,
285 tp->getLayout()).font,
286 buf->params.language);
291 #ifndef INHERIT_LANGUAGE
292 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
295 buf->params.language);
298 // Now, reduce font against full layout font
299 font.reduce(layoutfont);
301 par->setFont(pos, font);
305 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
306 Paragraph::size_type pos, LyXFont const & fnt)
310 LyXLayout const & layout =
311 textclasslist.Style(buf->params.textclass,
314 // Get concrete layout font to reduce against
317 if (pos < beginningOfMainBody(buf, par))
318 layoutfont = layout.labelfont;
320 layoutfont = layout.font;
322 // Realize against environment font information
323 if (par->getDepth()){
324 Paragraph * tp = par;
325 while (!layoutfont.resolved() && tp && tp->getDepth()) {
326 tp = tp->outerHook();
328 #ifndef INHERIT_LANGUAGE
329 layoutfont.realize(textclasslist.
330 Style(buf->params.textclass,
331 tp->getLayout()).font);
333 layoutfont.realize(textclasslist.
334 Style(buf->params.textclass,
335 tp->getLayout()).font,
336 buf->params.language);
341 #ifndef INHERIT_LANGUAGE
342 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
345 buf->params.language);
348 // Now, reduce font against full layout font
349 font.reduce(layoutfont);
351 par->setFont(pos, font);
355 // inserts a new row behind the specified row, increments
356 // the touched counters
357 void LyXText::insertRow(Row * row, Paragraph * par,
358 Paragraph::size_type pos) const
360 Row * tmprow = new Row;
363 tmprow->next(firstrow);
366 tmprow->previous(row);
367 tmprow->next(row->next());
372 tmprow->next()->previous(tmprow);
374 if (tmprow->previous())
375 tmprow->previous()->next(tmprow);
387 // removes the row and reset the touched counters
388 void LyXText::removeRow(Row * row) const
390 /* this must not happen before the currentrow for clear reasons.
391 so the trick is just to set the current row onto the previous
394 getRow(row->par(), row->pos(), unused_y);
397 row->next()->previous(row->previous());
398 if (!row->previous()) {
399 firstrow = row->next();
401 row->previous()->next(row->next());
404 lastrow = row->previous();
406 height -= row->height(); // the text becomes smaller
409 --number_of_rows; // one row less
413 // remove all following rows of the paragraph of the specified row.
414 void LyXText::removeParagraph(Row * row) const
416 Paragraph * tmppar = row->par();
420 while (row && row->par() == tmppar) {
421 tmprow = row->next();
428 // insert the specified paragraph behind the specified row
429 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
432 insertRow(row, par, 0); /* insert a new row, starting
435 setCounter(bview->buffer(), par); // set the counters
437 // and now append the whole paragraph behind the new row
440 appendParagraph(bview, firstrow);
442 row->next()->height(0);
443 appendParagraph(bview, row->next());
448 Inset * LyXText::getInset() const
451 if (cursor.pos() == 0 && cursor.par()->bibkey) {
452 inset = cursor.par()->bibkey;
453 } else if (cursor.pos() < cursor.par()->size()
454 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
455 inset = cursor.par()->getInset(cursor.pos());
461 void LyXText::toggleInset(BufferView * bview)
463 Inset * inset = getInset();
464 if (!inset->editable())
466 //bview->owner()->message(inset->editMessage());
468 // do we want to keep this?? (JMarc)
469 if (inset->editable() != Inset::HIGHLY_EDITABLE)
470 setCursorParUndo(bview);
472 if (inset->isOpen()) {
478 inset->open(bview, !inset->isOpen());
483 /* used in setlayout */
484 // Asger is not sure we want to do this...
485 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
488 LyXLayout const & layout =
489 textclasslist.Style(buf->params.textclass, par->getLayout());
492 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
493 if (pos < beginningOfMainBody(buf, par))
494 layoutfont = layout.labelfont;
496 layoutfont = layout.font;
498 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
499 tmpfont.reduce(layoutfont);
500 par->setFont(pos, tmpfont);
505 Paragraph * LyXText::setLayout(BufferView * bview,
506 LyXCursor & cur, LyXCursor & sstart_cur,
507 LyXCursor & send_cur,
508 LyXTextClass::size_type layout)
510 Paragraph * endpar = send_cur.par()->next();
511 Paragraph * undoendpar = endpar;
513 if (endpar && endpar->getDepth()) {
514 while (endpar && endpar->getDepth()) {
515 endpar = endpar->next();
519 endpar = endpar->next(); // because of parindents etc.
522 setUndo(bview, Undo::EDIT,
523 sstart_cur.par(), undoendpar);
525 // ok we have a selection. This is always between sstart_cur
526 // and sel_end cursor
529 LyXLayout const & lyxlayout =
530 textclasslist.Style(bview->buffer()->params.textclass, layout);
532 while (cur.par() != send_cur.par()) {
533 cur.par()->setLayout(layout);
534 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
535 Paragraph * fppar = cur.par();
536 fppar->params().spaceTop(lyxlayout.fill_top ?
537 VSpace(VSpace::VFILL)
538 : VSpace(VSpace::NONE));
539 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
540 VSpace(VSpace::VFILL)
541 : VSpace(VSpace::NONE));
542 if (lyxlayout.margintype == MARGIN_MANUAL)
543 cur.par()->setLabelWidthString(lyxlayout.labelstring());
544 if (lyxlayout.labeltype != LABEL_BIBLIO
546 delete fppar->bibkey;
549 cur.par(cur.par()->next());
551 cur.par()->setLayout(layout);
552 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
553 Paragraph * fppar = cur.par();
554 fppar->params().spaceTop(lyxlayout.fill_top ?
555 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
556 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
557 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
558 if (lyxlayout.margintype == MARGIN_MANUAL)
559 cur.par()->setLabelWidthString(lyxlayout.labelstring());
560 if (lyxlayout.labeltype != LABEL_BIBLIO
562 delete fppar->bibkey;
569 // set layout over selection and make a total rebreak of those paragraphs
570 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
572 LyXCursor tmpcursor = cursor; /* store the current cursor */
574 // if there is no selection just set the layout
575 // of the current paragraph */
576 if (!selection.set()) {
577 selection.start = cursor; // dummy selection
578 selection.end = cursor;
580 Paragraph * endpar = setLayout(bview, cursor, selection.start,
581 selection.end, layout);
582 redoParagraphs(bview, selection.start, endpar);
584 // we have to reset the selection, because the
585 // geometry could have changed
586 setCursor(bview, selection.start.par(),
587 selection.start.pos(), false);
588 selection.cursor = cursor;
589 setCursor(bview, selection.end.par(), selection.end.pos(), false);
590 updateCounters(bview, cursor.row());
593 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
597 // increment depth over selection and
598 // make a total rebreak of those paragraphs
599 void LyXText::incDepth(BufferView * bview)
601 // If there is no selection, just use the current paragraph
602 if (!selection.set()) {
603 selection.start = cursor; // dummy selection
604 selection.end = cursor;
607 // We end at the next paragraph with depth 0
608 Paragraph * endpar = selection.end.par()->next();
610 Paragraph * undoendpar = endpar;
612 if (endpar && endpar->getDepth()) {
613 while (endpar && endpar->getDepth()) {
614 endpar = endpar->next();
618 endpar = endpar->next(); // because of parindents etc.
621 setUndo(bview, Undo::EDIT,
622 selection.start.par(), undoendpar);
624 LyXCursor tmpcursor = cursor; // store the current cursor
626 // ok we have a selection. This is always between sel_start_cursor
627 // and sel_end cursor
628 cursor = selection.start;
630 bool anything_changed = false;
633 // NOTE: you can't change the depth of a bibliography entry
635 textclasslist.Style(bview->buffer()->params.textclass,
636 cursor.par()->getLayout()
637 ).labeltype != LABEL_BIBLIO) {
638 Paragraph * prev = cursor.par()->previous();
641 && (prev->getDepth() - cursor.par()->getDepth() > 0
642 || (prev->getDepth() == cursor.par()->getDepth()
643 && textclasslist.Style(bview->buffer()->params.textclass,
644 prev->getLayout()).isEnvironment()))) {
645 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
646 anything_changed = true;
649 if (cursor.par() == selection.end.par())
651 cursor.par(cursor.par()->next());
654 // if nothing changed set all depth to 0
655 if (!anything_changed) {
656 cursor = selection.start;
657 while (cursor.par() != selection.end.par()) {
658 cursor.par()->params().depth(0);
659 cursor.par(cursor.par()->next());
661 cursor.par()->params().depth(0);
664 redoParagraphs(bview, selection.start, endpar);
666 // we have to reset the selection, because the
667 // geometry could have changed
668 setCursor(bview, selection.start.par(), selection.start.pos());
669 selection.cursor = cursor;
670 setCursor(bview, selection.end.par(), selection.end.pos());
671 updateCounters(bview, cursor.row());
674 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
678 // decrement depth over selection and
679 // make a total rebreak of those paragraphs
680 void LyXText::decDepth(BufferView * bview)
682 // if there is no selection just set the layout
683 // of the current paragraph
684 if (!selection.set()) {
685 selection.start = cursor; // dummy selection
686 selection.end = cursor;
688 Paragraph * endpar = selection.end.par()->next();
689 Paragraph * undoendpar = endpar;
691 if (endpar && endpar->getDepth()) {
692 while (endpar && endpar->getDepth()) {
693 endpar = endpar->next();
697 endpar = endpar->next(); // because of parindents etc.
700 setUndo(bview, Undo::EDIT,
701 selection.start.par(), undoendpar);
703 LyXCursor tmpcursor = cursor; // store the current cursor
705 // ok we have a selection. This is always between sel_start_cursor
706 // and sel_end cursor
707 cursor = selection.start;
710 if (cursor.par()->params().depth()) {
711 cursor.par()->params()
712 .depth(cursor.par()->params().depth() - 1);
714 if (cursor.par() == selection.end.par()) {
717 cursor.par(cursor.par()->next());
720 redoParagraphs(bview, selection.start, endpar);
722 // we have to reset the selection, because the
723 // geometry could have changed
724 setCursor(bview, selection.start.par(),
725 selection.start.pos());
726 selection.cursor = cursor;
727 setCursor(bview, selection.end.par(), selection.end.pos());
728 updateCounters(bview, cursor.row());
731 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
735 // set font over selection and make a total rebreak of those paragraphs
736 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
738 // if there is no selection just set the current_font
739 if (!selection.set()) {
740 // Determine basis font
742 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
744 layoutfont = getLabelFont(bview->buffer(),
747 layoutfont = getLayoutFont(bview->buffer(),
750 // Update current font
751 real_current_font.update(font,
752 bview->buffer()->params.language,
755 // Reduce to implicit settings
756 current_font = real_current_font;
757 current_font.reduce(layoutfont);
758 // And resolve it completely
759 #ifndef INHERIT_LANGUAGE
760 real_current_font.realize(layoutfont);
762 real_current_font.realize(layoutfont,
763 bview->buffer()->params.language);
768 LyXCursor tmpcursor = cursor; // store the current cursor
770 // ok we have a selection. This is always between sel_start_cursor
771 // and sel_end cursor
773 setUndo(bview, Undo::EDIT,
774 selection.start.par(), selection.end.par()->next());
776 cursor = selection.start;
777 while (cursor.par() != selection.end.par() ||
778 (cursor.pos() < selection.end.pos()))
780 if (cursor.pos() < cursor.par()->size()) {
781 // an open footnote should behave
783 setCharFont(bview, cursor.par(), cursor.pos(),
785 cursor.pos(cursor.pos() + 1);
788 cursor.par(cursor.par()->next());
793 redoParagraphs(bview, selection.start, selection.end.par()->next());
795 // we have to reset the selection, because the
796 // geometry could have changed
797 setCursor(bview, selection.start.par(), selection.start.pos());
798 selection.cursor = cursor;
799 setCursor(bview, selection.end.par(), selection.end.pos());
802 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
803 tmpcursor.boundary());
807 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
809 Row * tmprow = cur.row();
810 int y = cur.y() - tmprow->baseline();
812 setHeightOfRow(bview, tmprow);
814 while (tmprow->previous()
815 && tmprow->previous()->par() == tmprow->par()) {
816 tmprow = tmprow->previous();
817 y -= tmprow->height();
818 setHeightOfRow(bview, tmprow);
821 // we can set the refreshing parameters now
822 status(bview, LyXText::NEED_MORE_REFRESH);
824 refresh_row = tmprow;
825 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
829 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
831 Row * tmprow = cur.row();
833 int y = cur.y() - tmprow->baseline();
834 setHeightOfRow(bview, tmprow);
836 while (tmprow->previous()
837 && tmprow->previous()->par() == tmprow->par()) {
838 tmprow = tmprow->previous();
839 y -= tmprow->height();
842 // we can set the refreshing parameters now
843 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
845 refresh_row = tmprow;
847 status(bview, LyXText::NEED_MORE_REFRESH);
848 setCursor(bview, cur.par(), cur.pos());
852 // deletes and inserts again all paragaphs between the cursor
853 // and the specified par
854 // This function is needed after SetLayout and SetFont etc.
855 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
856 Paragraph const * endpar) const
859 Paragraph * tmppar = 0;
860 Paragraph * first_phys_par = 0;
862 Row * tmprow = cur.row();
864 int y = cur.y() - tmprow->baseline();
866 if (!tmprow->previous()) {
867 // a trick/hack for UNDO
868 // Can somebody please tell me _why_ this solves
870 first_phys_par = firstParagraph();
872 first_phys_par = tmprow->par();
873 while (tmprow->previous()
874 && tmprow->previous()->par() == first_phys_par)
876 tmprow = tmprow->previous();
877 y -= tmprow->height();
881 // we can set the refreshing parameters now
882 status(bview, LyXText::NEED_MORE_REFRESH);
884 refresh_row = tmprow->previous(); /* the real refresh row will
885 be deleted, so I store
889 tmppar = tmprow->next()->par();
892 while (tmppar != endpar) {
893 removeRow(tmprow->next());
895 tmppar = tmprow->next()->par();
900 // remove the first one
901 tmprow2 = tmprow; /* this is because tmprow->previous()
903 tmprow = tmprow->previous();
906 tmppar = first_phys_par;
910 insertParagraph(bview, tmppar, tmprow);
914 while (tmprow->next()
915 && tmprow->next()->par() == tmppar) {
916 tmprow = tmprow->next();
918 tmppar = tmppar->next();
920 } while (tmppar && tmppar != endpar);
922 // this is because of layout changes
924 refresh_y -= refresh_row->height();
925 setHeightOfRow(bview, refresh_row);
927 refresh_row = firstrow;
929 setHeightOfRow(bview, refresh_row);
932 if (tmprow && tmprow->next())
933 setHeightOfRow(bview, tmprow->next());
937 bool LyXText::fullRebreak(BufferView * bview)
943 if (need_break_row) {
944 breakAgain(bview, need_break_row);
952 // important for the screen
955 /* the cursor set functions have a special mechanism. When they
956 * realize, that you left an empty paragraph, they will delete it.
957 * They also delete the corresponding row */
959 // need the selection cursor:
960 void LyXText::setSelection(BufferView * bview)
962 bool const lsel = selection.set();
964 if (!selection.set()) {
965 last_sel_cursor = selection.cursor;
966 selection.start = selection.cursor;
967 selection.end = selection.cursor;
972 // first the toggling area
973 if (cursor.y() < last_sel_cursor.y()
974 || (cursor.y() == last_sel_cursor.y()
975 && cursor.x() < last_sel_cursor.x())) {
976 toggle_end_cursor = last_sel_cursor;
977 toggle_cursor = cursor;
979 toggle_end_cursor = cursor;
980 toggle_cursor = last_sel_cursor;
983 last_sel_cursor = cursor;
985 // and now the whole selection
987 if (selection.cursor.par() == cursor.par())
988 if (selection.cursor.pos() < cursor.pos()) {
989 selection.end = cursor;
990 selection.start = selection.cursor;
992 selection.end = selection.cursor;
993 selection.start = cursor;
995 else if (selection.cursor.y() < cursor.y() ||
996 (selection.cursor.y() == cursor.y()
997 && selection.cursor.x() < cursor.x())) {
998 selection.end = cursor;
999 selection.start = selection.cursor;
1002 selection.end = selection.cursor;
1003 selection.start = cursor;
1006 // a selection with no contents is not a selection
1007 if (selection.start.par() == selection.end.par() &&
1008 selection.start.pos() == selection.end.pos())
1009 selection.set(false);
1011 if (inset_owner && (selection.set() || lsel))
1012 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1016 string const LyXText::selectionAsString(Buffer const * buffer) const
1018 if (!selection.set()) return string();
1021 // Special handling if the whole selection is within one paragraph
1022 if (selection.start.par() == selection.end.par()) {
1023 result += selection.start.par()->asString(buffer,
1024 selection.start.pos(),
1025 selection.end.pos());
1029 // The selection spans more than one paragraph
1031 // First paragraph in selection
1032 result += selection.start.par()->asString(buffer,
1033 selection.start.pos(),
1034 selection.start.par()->size())
1037 // The paragraphs in between (if any)
1038 LyXCursor tmpcur(selection.start);
1039 tmpcur.par(tmpcur.par()->next());
1040 while (tmpcur.par() != selection.end.par()) {
1041 result += tmpcur.par()->asString(buffer, 0,
1042 tmpcur.par()->size()) +"\n\n";
1043 tmpcur.par(tmpcur.par()->next());
1046 // Last paragraph in selection
1047 result += selection.end.par()->asString(buffer, 0,
1048 selection.end.pos());
1054 void LyXText::clearSelection() const
1056 selection.set(false);
1057 selection.mark(false);
1058 selection.end = selection.start = selection.cursor = cursor;
1062 void LyXText::cursorHome(BufferView * bview) const
1064 setCursor(bview, cursor.par(), cursor.row()->pos());
1068 void LyXText::cursorEnd(BufferView * bview) const
1070 if (!cursor.row()->next()
1071 || cursor.row()->next()->par() != cursor.row()->par()) {
1072 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1074 if (cursor.par()->size() &&
1075 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1076 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1077 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1079 setCursor(bview,cursor.par(),
1080 rowLast(cursor.row()) + 1);
1086 void LyXText::cursorTop(BufferView * bview) const
1088 while (cursor.par()->previous())
1089 cursor.par(cursor.par()->previous());
1090 setCursor(bview, cursor.par(), 0);
1094 void LyXText::cursorBottom(BufferView * bview) const
1096 while (cursor.par()->next())
1097 cursor.par(cursor.par()->next());
1098 setCursor(bview, cursor.par(), cursor.par()->size());
1102 void LyXText::toggleFree(BufferView * bview,
1103 LyXFont const & font, bool toggleall)
1105 // If the mask is completely neutral, tell user
1106 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1107 // Could only happen with user style
1108 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1112 // Try implicit word selection
1113 // If there is a change in the language the implicit word selection
1115 LyXCursor resetCursor = cursor;
1116 bool implicitSelection = (font.language() == ignore_language
1117 && font.number() == LyXFont::IGNORE)
1118 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1121 setFont(bview, font, toggleall);
1123 // Implicit selections are cleared afterwards
1124 //and cursor is set to the original position.
1125 if (implicitSelection) {
1127 cursor = resetCursor;
1128 setCursor(bview, cursor.par(), cursor.pos());
1129 selection.cursor = cursor;
1132 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1137 LyXText::getStringToIndex(BufferView * bview)
1141 // Try implicit word selection
1142 // If there is a change in the language the implicit word selection
1144 LyXCursor resetCursor = cursor;
1145 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1147 if (!selection.set()) {
1148 bview->owner()->message(_("Nothing to index!"));
1151 if (selection.start.par() != selection.end.par()) {
1152 bview->owner()->message(_("Cannot index more than one paragraph!"));
1156 idxstring = selectionAsString(bview->buffer());
1158 // Implicit selections are cleared afterwards
1159 //and cursor is set to the original position.
1160 if (implicitSelection) {
1162 cursor = resetCursor;
1163 setCursor(bview, cursor.par(), cursor.pos());
1164 selection.cursor = cursor;
1169 Paragraph::size_type
1170 LyXText::beginningOfMainBody(Buffer const * buf,
1171 Paragraph const * par) const
1173 if (textclasslist.Style(buf->params.textclass,
1174 par->getLayout()).labeltype != LABEL_MANUAL)
1177 return par->beginningOfMainBody();
1181 /* the DTP switches for paragraphs. LyX will store them in the
1182 * first physicla paragraph. When a paragraph is broken, the top settings
1183 * rest, the bottom settings are given to the new one. So I can make shure,
1184 * they do not duplicate themself and you cannnot make dirty things with
1187 void LyXText::setParagraph(BufferView * bview,
1188 bool line_top, bool line_bottom,
1189 bool pagebreak_top, bool pagebreak_bottom,
1190 VSpace const & space_top,
1191 VSpace const & space_bottom,
1192 Spacing const & spacing,
1194 string labelwidthstring,
1197 LyXCursor tmpcursor = cursor;
1198 if (!selection.set()) {
1199 selection.start = cursor;
1200 selection.end = cursor;
1203 // make sure that the depth behind the selection are restored, too
1204 Paragraph * endpar = selection.end.par()->next();
1205 Paragraph * undoendpar = endpar;
1207 if (endpar && endpar->getDepth()) {
1208 while (endpar && endpar->getDepth()) {
1209 endpar = endpar->next();
1210 undoendpar = endpar;
1214 // because of parindents etc.
1215 endpar = endpar->next();
1218 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1221 Paragraph * tmppar = selection.end.par();
1222 while (tmppar != selection.start.par()->previous()) {
1223 setCursor(bview, tmppar, 0);
1224 status(bview, LyXText::NEED_MORE_REFRESH);
1225 refresh_row = cursor.row();
1226 refresh_y = cursor.y() - cursor.row()->baseline();
1227 cursor.par()->params().lineTop(line_top);
1228 cursor.par()->params().lineBottom(line_bottom);
1229 cursor.par()->params().pagebreakTop(pagebreak_top);
1230 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1231 cursor.par()->params().spaceTop(space_top);
1232 cursor.par()->params().spaceBottom(space_bottom);
1233 cursor.par()->params().spacing(spacing);
1234 // does the layout allow the new alignment?
1235 if (align == LYX_ALIGN_LAYOUT)
1236 align = textclasslist
1237 .Style(bview->buffer()->params.textclass,
1238 cursor.par()->getLayout()).align;
1239 if (align & textclasslist
1240 .Style(bview->buffer()->params.textclass,
1241 cursor.par()->getLayout()).alignpossible) {
1242 if (align == textclasslist
1243 .Style(bview->buffer()->params.textclass,
1244 cursor.par()->getLayout()).align)
1245 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1247 cursor.par()->params().align(align);
1249 cursor.par()->setLabelWidthString(labelwidthstring);
1250 cursor.par()->params().noindent(noindent);
1251 tmppar = cursor.par()->previous();
1254 redoParagraphs(bview, selection.start, endpar);
1257 setCursor(bview, selection.start.par(), selection.start.pos());
1258 selection.cursor = cursor;
1259 setCursor(bview, selection.end.par(), selection.end.pos());
1260 setSelection(bview);
1261 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1263 bview->updateInset(inset_owner, true);
1267 char loweralphaCounter(int n)
1269 if (n < 1 || n > 26)
1279 char alphaCounter(int n)
1281 if (n < 1 || n > 26)
1289 char hebrewCounter(int n)
1291 static const char hebrew[22] = {
1292 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1293 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1294 '÷', 'ø', 'ù', 'ú'
1296 if (n < 1 || n > 22)
1304 string const romanCounter(int n)
1306 static char const * roman[20] = {
1307 "i", "ii", "iii", "iv", "v",
1308 "vi", "vii", "viii", "ix", "x",
1309 "xi", "xii", "xiii", "xiv", "xv",
1310 "xvi", "xvii", "xviii", "xix", "xx"
1312 if (n < 1 || n > 20)
1321 // set the counter of a paragraph. This includes the labels
1322 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1324 LyXLayout const & layout =
1325 textclasslist.Style(buf->params.textclass,
1328 LyXTextClass const & textclass =
1329 textclasslist.TextClass(buf->params.textclass);
1331 // copy the prev-counters to this one,
1332 // unless this is the first paragraph
1333 if (par->previous()) {
1334 for (int i = 0; i < 10; ++i) {
1335 par->setCounter(i, par->previous()->getFirstCounter(i));
1337 par->params().appendix(par->previous()->params().appendix());
1338 if (!par->params().appendix() && par->params().startOfAppendix()) {
1339 par->params().appendix(true);
1340 for (int i = 0; i < 10; ++i) {
1341 par->setCounter(i, 0);
1344 par->enumdepth = par->previous()->enumdepth;
1345 par->itemdepth = par->previous()->itemdepth;
1347 for (int i = 0; i < 10; ++i) {
1348 par->setCounter(i, 0);
1350 par->params().appendix(par->params().startOfAppendix());
1355 /* Maybe we have to increment the enumeration depth.
1356 * BUT, enumeration in a footnote is considered in isolation from its
1357 * surrounding paragraph so don't increment if this is the
1358 * first line of the footnote
1359 * AND, bibliographies can't have their depth changed ie. they
1360 * are always of depth 0
1363 && par->previous()->getDepth() < par->getDepth()
1364 && textclasslist.Style(buf->params.textclass,
1365 par->previous()->getLayout()
1366 ).labeltype == LABEL_COUNTER_ENUMI
1367 && par->enumdepth < 3
1368 && layout.labeltype != LABEL_BIBLIO) {
1372 // Maybe we have to decrement the enumeration depth, see note above
1374 && par->previous()->getDepth() > par->getDepth()
1375 && layout.labeltype != LABEL_BIBLIO) {
1376 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1377 par->setCounter(6 + par->enumdepth,
1378 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1379 /* reset the counters.
1380 * A depth change is like a breaking layout
1382 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1383 par->setCounter(i, 0);
1386 if (!par->params().labelString().empty()) {
1387 par->params().labelString(string());
1390 if (layout.margintype == MARGIN_MANUAL) {
1391 if (par->params().labelWidthString().empty()) {
1392 par->setLabelWidthString(layout.labelstring());
1395 par->setLabelWidthString(string());
1398 // is it a layout that has an automatic label?
1399 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1401 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1402 if (i >= 0 && i<= buf->params.secnumdepth) {
1403 par->incCounter(i); // increment the counter
1405 // Is there a label? Useful for Chapter layout
1406 if (!par->params().appendix()) {
1407 if (!layout.labelstring().empty())
1408 par->params().labelString(layout.labelstring());
1410 par->params().labelString(string());
1412 if (!layout.labelstring_appendix().empty())
1413 par->params().labelString(layout.labelstring_appendix());
1415 par->params().labelString(string());
1420 if (!par->params().appendix()) {
1421 switch (2 * LABEL_COUNTER_CHAPTER -
1422 textclass.maxcounter() + i) {
1423 case LABEL_COUNTER_CHAPTER:
1424 s << par->getCounter(i);
1426 case LABEL_COUNTER_SECTION:
1427 s << par->getCounter(i - 1) << '.'
1428 << par->getCounter(i);
1430 case LABEL_COUNTER_SUBSECTION:
1431 s << par->getCounter(i - 2) << '.'
1432 << par->getCounter(i - 1) << '.'
1433 << par->getCounter(i);
1435 case LABEL_COUNTER_SUBSUBSECTION:
1436 s << par->getCounter(i - 3) << '.'
1437 << par->getCounter(i - 2) << '.'
1438 << par->getCounter(i - 1) << '.'
1439 << par->getCounter(i);
1442 case LABEL_COUNTER_PARAGRAPH:
1443 s << par->getCounter(i - 4) << '.'
1444 << par->getCounter(i - 3) << '.'
1445 << par->getCounter(i - 2) << '.'
1446 << par->getCounter(i - 1) << '.'
1447 << par->getCounter(i);
1449 case LABEL_COUNTER_SUBPARAGRAPH:
1450 s << par->getCounter(i - 5) << '.'
1451 << par->getCounter(i - 4) << '.'
1452 << par->getCounter(i - 3) << '.'
1453 << par->getCounter(i - 2) << '.'
1454 << par->getCounter(i - 1) << '.'
1455 << par->getCounter(i);
1459 // Can this ever be reached? And in the
1460 // case it is, how can this be correct?
1462 s << par->getCounter(i) << '.';
1465 } else { // appendix
1466 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1467 case LABEL_COUNTER_CHAPTER:
1468 if (par->isRightToLeftPar(buf->params))
1469 s << hebrewCounter(par->getCounter(i));
1471 s << alphaCounter(par->getCounter(i));
1473 case LABEL_COUNTER_SECTION:
1474 if (par->isRightToLeftPar(buf->params))
1475 s << hebrewCounter(par->getCounter(i - 1));
1477 s << alphaCounter(par->getCounter(i - 1));
1480 << par->getCounter(i);
1483 case LABEL_COUNTER_SUBSECTION:
1484 if (par->isRightToLeftPar(buf->params))
1485 s << hebrewCounter(par->getCounter(i - 2));
1487 s << alphaCounter(par->getCounter(i - 2));
1490 << par->getCounter(i-1) << '.'
1491 << par->getCounter(i);
1494 case LABEL_COUNTER_SUBSUBSECTION:
1495 if (par->isRightToLeftPar(buf->params))
1496 s << hebrewCounter(par->getCounter(i-3));
1498 s << alphaCounter(par->getCounter(i-3));
1501 << par->getCounter(i-2) << '.'
1502 << par->getCounter(i-1) << '.'
1503 << par->getCounter(i);
1506 case LABEL_COUNTER_PARAGRAPH:
1507 if (par->isRightToLeftPar(buf->params))
1508 s << hebrewCounter(par->getCounter(i-4));
1510 s << alphaCounter(par->getCounter(i-4));
1513 << par->getCounter(i-3) << '.'
1514 << par->getCounter(i-2) << '.'
1515 << par->getCounter(i-1) << '.'
1516 << par->getCounter(i);
1519 case LABEL_COUNTER_SUBPARAGRAPH:
1520 if (par->isRightToLeftPar(buf->params))
1521 s << hebrewCounter(par->getCounter(i-5));
1523 s << alphaCounter(par->getCounter(i-5));
1526 << par->getCounter(i-4) << '.'
1527 << par->getCounter(i-3) << '.'
1528 << par->getCounter(i-2) << '.'
1529 << par->getCounter(i-1) << '.'
1530 << par->getCounter(i);
1534 // Can this ever be reached? And in the
1535 // case it is, how can this be correct?
1537 s << par->getCounter(i) << '.';
1543 par->params().labelString(par->params().labelString() +s.str().c_str());
1544 // We really want to remove the c_str as soon as
1547 for (i++; i < 10; ++i) {
1548 // reset the following counters
1549 par->setCounter(i, 0);
1551 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1552 for (i++; i < 10; ++i) {
1553 // reset the following counters
1554 par->setCounter(i, 0);
1556 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1557 par->incCounter(i + par->enumdepth);
1558 int number = par->getCounter(i + par->enumdepth);
1562 switch (par->enumdepth) {
1564 if (par->isRightToLeftPar(buf->params))
1566 << hebrewCounter(number)
1570 << loweralphaCounter(number)
1574 if (par->isRightToLeftPar(buf->params))
1575 s << '.' << romanCounter(number);
1577 s << romanCounter(number) << '.';
1580 if (par->isRightToLeftPar(buf->params))
1582 << alphaCounter(number);
1584 s << alphaCounter(number)
1588 if (par->isRightToLeftPar(buf->params))
1595 par->params().labelString(s.str().c_str());
1597 for (i += par->enumdepth + 1; i < 10; ++i) {
1598 // reset the following counters
1599 par->setCounter(i, 0);
1603 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1604 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1606 int number = par->getCounter(i);
1608 InsetCommandParams p( "bibitem" );
1609 par->bibkey = new InsetBibKey(p);
1611 par->bibkey->setCounter(number);
1612 par->params().labelString(layout.labelstring());
1614 // In biblio should't be following counters but...
1616 string s = layout.labelstring();
1618 // the caption hack:
1619 if (layout.labeltype == LABEL_SENSITIVE) {
1620 bool isOK (par->inInset() && par->inInset()->owner() &&
1621 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1624 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1626 = floatList.getType(tmp->type());
1627 // We should get the correct number here too.
1628 s = fl.name() + " #:";
1630 /* par->SetLayout(0);
1631 s = layout->labelstring; */
1632 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1633 ? " :úåòîùî øñç" : "Senseless: ";
1636 par->params().labelString(s);
1638 /* reset the enumeration counter. They are always resetted
1639 * when there is any other layout between */
1640 for (int i = 6 + par->enumdepth; i < 10; ++i)
1641 par->setCounter(i, 0);
1646 // Updates all counters BEHIND the row. Changed paragraphs
1647 // with a dynamic left margin will be rebroken.
1648 void LyXText::updateCounters(BufferView * bview, Row * row) const
1656 par = row->par()->next();
1660 while (row->par() != par)
1663 setCounter(bview->buffer(), par);
1665 // now check for the headline layouts. remember that they
1666 // have a dynamic left margin
1667 if ((textclasslist.Style(bview->buffer()->params.textclass,
1668 par->layout).margintype == MARGIN_DYNAMIC
1669 || textclasslist.Style(bview->buffer()->params.textclass,
1670 par->layout).labeltype == LABEL_SENSITIVE)) {
1672 // Rebreak the paragraph
1673 removeParagraph(row);
1674 appendParagraph(bview, row);
1681 void LyXText::insertInset(BufferView * bview, Inset * inset)
1683 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1685 setUndo(bview, Undo::INSERT,
1686 cursor.par(), cursor.par()->next());
1687 cursor.par()->insertInset(cursor.pos(), inset);
1688 // Just to rebreak and refresh correctly.
1689 // The character will not be inserted a second time
1690 insertChar(bview, Paragraph::META_INSET);
1692 // If we enter a highly editable inset the cursor should be to before
1693 // the inset. This couldn't happen before as Undo was not handled inside
1694 // inset now after the Undo LyX tries to call inset->Edit(...) again
1695 // and cannot do this as the cursor is behind the inset and GetInset
1696 // does not return the inset!
1697 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1698 cursorLeft(bview, true);
1704 void LyXText::copyEnvironmentType()
1706 copylayouttype = cursor.par()->getLayout();
1710 void LyXText::pasteEnvironmentType(BufferView * bview)
1712 setLayout(bview, copylayouttype);
1716 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1718 // Stuff what we got on the clipboard. Even if there is no selection.
1720 // There is a problem with having the stuffing here in that the
1721 // larger the selection the slower LyX will get. This can be
1722 // solved by running the line below only when the selection has
1723 // finished. The solution used currently just works, to make it
1724 // faster we need to be more clever and probably also have more
1725 // calls to stuffClipboard. (Lgb)
1726 bview->stuffClipboard(selectionAsString(bview->buffer()));
1728 // This doesn't make sense, if there is no selection
1729 if (!selection.set())
1732 // OK, we have a selection. This is always between selection.start
1733 // and selection.end
1735 // make sure that the depth behind the selection are restored, too
1736 Paragraph * endpar = selection.end.par()->next();
1737 Paragraph * undoendpar = endpar;
1739 if (endpar && endpar->getDepth()) {
1740 while (endpar && endpar->getDepth()) {
1741 endpar = endpar->next();
1742 undoendpar = endpar;
1744 } else if (endpar) {
1745 endpar = endpar->next(); // because of parindents etc.
1748 setUndo(bview, Undo::DELETE,
1749 selection.start.par(), undoendpar);
1751 // there are two cases: cut only within one paragraph or
1752 // more than one paragraph
1753 if (selection.start.par() == selection.end.par()) {
1754 // only within one paragraph
1755 endpar = selection.end.par();
1756 int pos = selection.end.pos();
1757 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1758 selection.start.pos(), pos,
1759 bview->buffer()->params.textclass, doclear,
1761 selection.end.pos(pos);
1763 endpar = selection.end.par();
1764 int pos = selection.end.pos();
1765 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1766 selection.start.pos(), pos,
1767 bview->buffer()->params.textclass, doclear,
1770 selection.end.par(endpar);
1771 selection.end.pos(pos);
1772 cursor.pos(selection.end.pos());
1774 endpar = endpar->next();
1776 // sometimes necessary
1778 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1780 redoParagraphs(bview, selection.start, endpar);
1782 // cutSelection can invalidate the cursor so we need to set
1784 cursor = selection.start;
1786 // need a valid cursor. (Lgb)
1789 setCursor(bview, cursor.par(), cursor.pos());
1790 selection.cursor = cursor;
1791 updateCounters(bview, cursor.row());
1795 void LyXText::copySelection(BufferView * bview)
1797 // Stuff what we got on the clipboard. Even if there is no selection.
1799 // There is a problem with having the stuffing here in that the
1800 // larger the selection the slower LyX will get. This can be
1801 // solved by running the line below only when the selection has
1802 // finished. The solution used currently just works, to make it
1803 // faster we need to be more clever and probably also have more
1804 // calls to stuffClipboard. (Lgb)
1805 bview->stuffClipboard(selectionAsString(bview->buffer()));
1807 // this doesnt make sense, if there is no selection
1808 if (!selection.set())
1811 // ok we have a selection. This is always between selection.start
1812 // and sel_end cursor
1814 // copy behind a space if there is one
1815 while (selection.start.par()->size() > selection.start.pos()
1816 && selection.start.par()->isLineSeparator(selection.start.pos())
1817 && (selection.start.par() != selection.end.par()
1818 || selection.start.pos() < selection.end.pos()))
1819 selection.start.pos(selection.start.pos() + 1);
1821 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1822 selection.start.pos(), selection.end.pos(),
1823 bview->buffer()->params.textclass);
1827 void LyXText::pasteSelection(BufferView * bview)
1829 // this does not make sense, if there is nothing to paste
1830 if (!CutAndPaste::checkPastePossible(cursor.par()))
1833 setUndo(bview, Undo::INSERT,
1834 cursor.par(), cursor.par()->next());
1837 Paragraph * actpar = cursor.par();
1839 int pos = cursor.pos();
1840 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1841 bview->buffer()->params.textclass);
1843 redoParagraphs(bview, cursor, endpar);
1845 setCursor(bview, cursor.par(), cursor.pos());
1848 selection.cursor = cursor;
1849 setCursor(bview, actpar, pos);
1850 setSelection(bview);
1851 updateCounters(bview, cursor.row());
1855 // returns a pointer to the very first Paragraph
1856 Paragraph * LyXText::firstParagraph() const
1858 return ownerParagraph();
1862 // sets the selection over the number of characters of string, no check!!
1863 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1868 selection.cursor = cursor;
1869 for (string::size_type i = 0; i < str.length(); ++i)
1871 setSelection(bview);
1875 // simple replacing. The font of the first selected character is used
1876 void LyXText::replaceSelectionWithString(BufferView * bview,
1879 setCursorParUndo(bview);
1882 if (!selection.set()) { // create a dummy selection
1883 selection.end = cursor;
1884 selection.start = cursor;
1887 // Get font setting before we cut
1888 Paragraph::size_type pos = selection.end.pos();
1889 LyXFont const font = selection.start.par()
1890 ->getFontSettings(bview->buffer()->params,
1891 selection.start.pos());
1893 // Insert the new string
1894 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1895 selection.end.par()->insertChar(pos, (*cit), font);
1899 // Cut the selection
1900 cutSelection(bview, true, false);
1906 // needed to insert the selection
1907 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1909 Paragraph * par = cursor.par();
1910 Paragraph::size_type pos = cursor.pos();
1911 Paragraph * endpar = cursor.par()->next();
1913 setCursorParUndo(bview);
1915 // only to be sure, should not be neccessary
1918 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1920 redoParagraphs(bview, cursor, endpar);
1921 setCursor(bview, cursor.par(), cursor.pos());
1922 selection.cursor = cursor;
1923 setCursor(bview, par, pos);
1924 setSelection(bview);
1928 // turns double-CR to single CR, others where converted into one
1929 // blank. Then InsertStringAsLines is called
1930 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1932 string linestr(str);
1933 bool newline_inserted = false;
1934 for (string::size_type i = 0; i < linestr.length(); ++i) {
1935 if (linestr[i] == '\n') {
1936 if (newline_inserted) {
1937 // we know that \r will be ignored by
1938 // InsertStringA. Of course, it is a dirty
1939 // trick, but it works...
1940 linestr[i - 1] = '\r';
1944 newline_inserted = true;
1946 } else if (IsPrintable(linestr[i])) {
1947 newline_inserted = false;
1950 insertStringAsLines(bview, linestr);
1954 bool LyXText::gotoNextInset(BufferView * bview,
1955 std::vector<Inset::Code> const & codes,
1956 string const & contents) const
1958 LyXCursor res = cursor;
1961 if (res.pos() < res.par()->size() - 1) {
1962 res.pos(res.pos() + 1);
1964 res.par(res.par()->next());
1968 } while (res.par() &&
1969 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1970 && (inset = res.par()->getInset(res.pos())) != 0
1971 && find(codes.begin(), codes.end(), inset->lyxCode())
1973 && (contents.empty() ||
1974 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1978 setCursor(bview, res.par(), res.pos());
1985 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1986 Paragraph::size_type pos)
1988 LyXCursor tmpcursor;
1991 Paragraph::size_type z;
1992 Row * row = getRow(par, pos, y);
1994 // is there a break one row above
1995 if (row->previous() && row->previous()->par() == row->par()) {
1996 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1997 if (z >= row->pos()) {
1998 // set the dimensions of the row above
1999 y -= row->previous()->height();
2001 refresh_row = row->previous();
2002 status(bview, LyXText::NEED_MORE_REFRESH);
2004 breakAgain(bview, row->previous());
2006 // set the cursor again. Otherwise
2007 // dangling pointers are possible
2008 setCursor(bview, cursor.par(), cursor.pos(),
2009 false, cursor.boundary());
2010 selection.cursor = cursor;
2015 int const tmpheight = row->height();
2016 Paragraph::size_type const tmplast = rowLast(row);
2020 breakAgain(bview, row);
2021 if (row->height() == tmpheight && rowLast(row) == tmplast)
2022 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2024 status(bview, LyXText::NEED_MORE_REFRESH);
2026 // check the special right address boxes
2027 if (textclasslist.Style(bview->buffer()->params.textclass,
2028 par->getLayout()).margintype
2029 == MARGIN_RIGHT_ADDRESS_BOX) {
2036 redoDrawingOfParagraph(bview, tmpcursor);
2039 // set the cursor again. Otherwise dangling pointers are possible
2040 // also set the selection
2042 if (selection.set()) {
2044 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2045 false, selection.cursor.boundary());
2046 selection.cursor = cursor;
2047 setCursorIntern(bview, selection.start.par(),
2048 selection.start.pos(),
2049 false, selection.start.boundary());
2050 selection.start = cursor;
2051 setCursorIntern(bview, selection.end.par(),
2052 selection.end.pos(),
2053 false, selection.end.boundary());
2054 selection.end = cursor;
2055 setCursorIntern(bview, last_sel_cursor.par(),
2056 last_sel_cursor.pos(),
2057 false, last_sel_cursor.boundary());
2058 last_sel_cursor = cursor;
2061 setCursorIntern(bview, cursor.par(), cursor.pos(),
2062 false, cursor.boundary());
2066 // returns false if inset wasn't found
2067 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2069 // first check the current paragraph
2070 int pos = cursor.par()->getPositionOfInset(inset);
2072 checkParagraph(bview, cursor.par(), pos);
2076 // check every paragraph
2078 Paragraph * par = firstParagraph();
2080 pos = par->getPositionOfInset(inset);
2082 checkParagraph(bview, par, pos);
2092 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2093 Paragraph::size_type pos,
2094 bool setfont, bool boundary) const
2096 LyXCursor old_cursor = cursor;
2097 setCursorIntern(bview, par, pos, setfont, boundary);
2098 deleteEmptyParagraphMechanism(bview, old_cursor);
2102 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2103 Paragraph::size_type pos, bool boundary) const
2107 cur.boundary(boundary);
2109 // get the cursor y position in text
2111 Row * row = getRow(par, pos, y);
2112 // y is now the beginning of the cursor row
2113 y += row->baseline();
2114 // y is now the cursor baseline
2117 // now get the cursors x position
2119 float fill_separator;
2121 float fill_label_hfill;
2122 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2124 Paragraph::size_type cursor_vpos = 0;
2125 Paragraph::size_type last = rowLastPrintable(row);
2127 if (pos > last + 1) // This shouldn't happen.
2129 else if (pos < row->pos())
2132 if (last < row->pos())
2133 cursor_vpos = row->pos();
2134 else if (pos > last && !boundary)
2135 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2136 ? row->pos() : last + 1;
2137 else if (pos > row->pos() &&
2138 (pos > last || boundary))
2139 /// Place cursor after char at (logical) position pos - 1
2140 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2141 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2143 /// Place cursor before char at (logical) position pos
2144 cursor_vpos = (bidi_level(pos) % 2 == 0)
2145 ? log2vis(pos) : log2vis(pos) + 1;
2147 Paragraph::size_type main_body =
2148 beginningOfMainBody(bview->buffer(), row->par());
2149 if ((main_body > 0) &&
2150 ((main_body-1 > last) ||
2151 !row->par()->isLineSeparator(main_body-1)))
2154 for (Paragraph::size_type vpos = row->pos();
2155 vpos < cursor_vpos; ++vpos) {
2156 pos = vis2log(vpos);
2157 if (main_body > 0 && pos == main_body - 1) {
2158 x += fill_label_hfill +
2159 lyxfont::width(textclasslist.Style(
2160 bview->buffer()->params.textclass,
2161 row->par()->getLayout())
2163 getLabelFont(bview->buffer(), row->par()));
2164 if (row->par()->isLineSeparator(main_body-1))
2165 x -= singleWidth(bview, row->par(),main_body-1);
2167 if (hfillExpansion(bview->buffer(), row, pos)) {
2168 x += singleWidth(bview, row->par(), pos);
2169 if (pos >= main_body)
2172 x += fill_label_hfill;
2173 } else if (row->par()->isSeparator(pos)) {
2174 x += singleWidth(bview, row->par(), pos);
2175 if (pos >= main_body)
2176 x += fill_separator;
2178 x += singleWidth(bview, row->par(), pos);
2187 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2188 Paragraph::size_type pos,
2189 bool setfont, bool boundary) const
2191 InsetText * it = static_cast<InsetText *>(par->inInset());
2193 if (it != inset_owner) {
2194 lyxerr << "InsetText is " << it << endl;
2195 lyxerr << "inset_owner is " << inset_owner << endl;
2196 #warning I belive this code is wrong. (Lgb)
2197 #warning Jürgen, have a look at this. (Lgb)
2198 #warning Hmmm, I guess you are right but we
2199 #warning should verify when this is needed
2200 // Jürgen, would you like to have a look?
2201 // I guess we need to move the outer cursor
2202 // and open and lock the inset (bla bla bla)
2203 // stuff I don't know... so can you have a look?
2205 // I moved the lyxerr stuff in here so we can see if this
2206 // is actually really needed and where!
2208 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2214 setCursor(bview, cursor, par, pos, boundary);
2216 setCurrentFont(bview);
2220 void LyXText::setCurrentFont(BufferView * bview) const
2222 Paragraph::size_type pos = cursor.pos();
2223 if (cursor.boundary() && pos > 0)
2227 if (pos == cursor.par()->size())
2229 else // potentional bug... BUG (Lgb)
2230 if (cursor.par()->isSeparator(pos)) {
2231 if (pos > cursor.row()->pos() &&
2232 bidi_level(pos) % 2 ==
2233 bidi_level(pos - 1) % 2)
2235 else if (pos + 1 < cursor.par()->size())
2241 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2242 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2244 if (cursor.pos() == cursor.par()->size() &&
2245 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2246 !cursor.boundary()) {
2247 Language const * lang =
2248 cursor.par()->getParLanguage(bview->buffer()->params);
2249 current_font.setLanguage(lang);
2250 current_font.setNumber(LyXFont::OFF);
2251 real_current_font.setLanguage(lang);
2252 real_current_font.setNumber(LyXFont::OFF);
2257 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2259 LyXCursor old_cursor = cursor;
2261 setCursorFromCoordinates(bview, cursor, x, y);
2262 setCurrentFont(bview);
2263 deleteEmptyParagraphMechanism(bview, old_cursor);
2267 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2270 // Get the row first.
2272 Row * row = getRowNearY(y);
2274 int const column = getColumnNearX(bview, row, x, bound);
2276 cur.par(row->par());
2277 cur.pos(row->pos() + column);
2279 cur.y(y + row->baseline());
2281 cur.boundary(bound);
2285 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2287 if (cursor.pos() > 0) {
2288 bool boundary = cursor.boundary();
2289 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2290 if (!internal && !boundary &&
2291 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2292 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2293 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2294 Paragraph * par = cursor.par()->previous();
2295 setCursor(bview, par, par->size());
2300 void LyXText::cursorRight(BufferView * bview, bool internal) const
2302 if (!internal && cursor.boundary() &&
2303 !cursor.par()->isNewline(cursor.pos()))
2304 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2305 else if (cursor.pos() < cursor.par()->size()) {
2306 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2308 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2309 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2310 } else if (cursor.par()->next())
2311 setCursor(bview, cursor.par()->next(), 0);
2315 void LyXText::cursorUp(BufferView * bview) const
2317 setCursorFromCoordinates(bview, cursor.x_fix(),
2318 cursor.y() - cursor.row()->baseline() - 1);
2322 void LyXText::cursorDown(BufferView * bview) const
2324 setCursorFromCoordinates(bview, cursor.x_fix(),
2325 cursor.y() - cursor.row()->baseline()
2326 + cursor.row()->height() + 1);
2330 void LyXText::cursorUpParagraph(BufferView * bview) const
2332 if (cursor.pos() > 0) {
2333 setCursor(bview, cursor.par(), 0);
2335 else if (cursor.par()->previous()) {
2336 setCursor(bview, cursor.par()->previous(), 0);
2341 void LyXText::cursorDownParagraph(BufferView * bview) const
2343 if (cursor.par()->next()) {
2344 setCursor(bview, cursor.par()->next(), 0);
2346 setCursor(bview, cursor.par(), cursor.par()->size());
2351 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2352 LyXCursor const & old_cursor) const
2354 // Would be wrong to delete anything if we have a selection.
2355 if (selection.set()) return;
2357 // We allow all kinds of "mumbo-jumbo" when freespacing.
2358 if (textclasslist.Style(bview->buffer()->params.textclass,
2359 old_cursor.par()->getLayout()).free_spacing)
2362 bool deleted = false;
2364 /* Ok I'll put some comments here about what is missing.
2365 I have fixed BackSpace (and thus Delete) to not delete
2366 double-spaces automagically. I have also changed Cut,
2367 Copy and Paste to hopefully do some sensible things.
2368 There are still some small problems that can lead to
2369 double spaces stored in the document file or space at
2370 the beginning of paragraphs. This happens if you have
2371 the cursor betwenn to spaces and then save. Or if you
2372 cut and paste and the selection have a space at the
2373 beginning and then save right after the paste. I am
2374 sure none of these are very hard to fix, but I will
2375 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2376 that I can get some feedback. (Lgb)
2379 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2380 // delete the LineSeparator.
2383 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2384 // delete the LineSeparator.
2387 // If the pos around the old_cursor were spaces, delete one of them.
2388 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2389 // Only if the cursor has really moved
2391 if (old_cursor.pos() > 0
2392 && old_cursor.pos() < old_cursor.par()->size()
2393 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2394 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2395 old_cursor.par()->erase(old_cursor.pos() - 1);
2396 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2398 if (old_cursor.par() == cursor.par() &&
2399 cursor.pos() > old_cursor.pos()) {
2400 setCursorIntern(bview, cursor.par(),
2403 setCursorIntern(bview, cursor.par(),
2409 // Do not delete empty paragraphs with keepempty set.
2410 if ((textclasslist.Style(bview->buffer()->params.textclass,
2411 old_cursor.par()->getLayout())).keepempty)
2414 LyXCursor tmpcursor;
2416 if (old_cursor.par() != cursor.par()) {
2417 if ((old_cursor.par()->size() == 0
2418 || (old_cursor.par()->size() == 1
2419 && old_cursor.par()->isLineSeparator(0)))) {
2420 // ok, we will delete anything
2422 // make sure that you do not delete any environments
2423 status(bview, LyXText::NEED_MORE_REFRESH);
2426 if (old_cursor.row()->previous()) {
2427 refresh_row = old_cursor.row()->previous();
2428 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2430 cursor = old_cursor; // that undo can restore the right cursor position
2431 Paragraph * endpar = old_cursor.par()->next();
2432 if (endpar && endpar->getDepth()) {
2433 while (endpar && endpar->getDepth()) {
2434 endpar = endpar->next();
2437 setUndo(bview, Undo::DELETE,
2443 removeRow(old_cursor.row());
2444 if (ownerParagraph() == old_cursor.par()) {
2445 ownerParagraph(ownerParagraph()->next());
2448 delete old_cursor.par();
2450 /* Breakagain the next par. Needed
2451 * because of the parindent that
2452 * can occur or dissappear. The
2453 * next row can change its height,
2454 * if there is another layout before */
2455 if (refresh_row->next()) {
2456 breakAgain(bview, refresh_row->next());
2457 updateCounters(bview, refresh_row);
2459 setHeightOfRow(bview, refresh_row);
2461 refresh_row = old_cursor.row()->next();
2462 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2465 cursor = old_cursor; // that undo can restore the right cursor position
2466 Paragraph * endpar = old_cursor.par()->next();
2467 if (endpar && endpar->getDepth()) {
2468 while (endpar && endpar->getDepth()) {
2469 endpar = endpar->next();
2472 setUndo(bview, Undo::DELETE,
2478 removeRow(old_cursor.row());
2480 if (ownerParagraph() == old_cursor.par()) {
2481 ownerParagraph(ownerParagraph()->next());
2484 delete old_cursor.par();
2486 /* Breakagain the next par. Needed
2487 because of the parindent that can
2488 occur or dissappear.
2489 The next row can change its height,
2490 if there is another layout before
2493 breakAgain(bview, refresh_row);
2494 updateCounters(bview, refresh_row->previous());
2500 setCursorIntern(bview, cursor.par(), cursor.pos());
2502 if (selection.cursor.par() == old_cursor.par()
2503 && selection.cursor.pos() == selection.cursor.pos()) {
2504 // correct selection
2505 selection.cursor = cursor;
2509 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2510 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2512 setCursorIntern(bview, cursor.par(), cursor.pos());
2513 selection.cursor = cursor;
2520 void LyXText::toggleAppendix(BufferView * bview)
2522 Paragraph * par = cursor.par();
2523 bool start = !par->params().startOfAppendix();
2525 // ensure that we have only one start_of_appendix in this document
2526 Paragraph * tmp = firstParagraph();
2527 for (; tmp; tmp = tmp->next()) {
2528 tmp->params().startOfAppendix(false);
2531 par->params().startOfAppendix(start);
2533 // we can set the refreshing parameters now
2534 status(bview, LyXText::NEED_MORE_REFRESH);
2536 refresh_row = 0; // not needed for full update
2537 updateCounters(bview, 0);
2538 setCursor(bview, cursor.par(), cursor.pos());
2542 Paragraph * LyXText::ownerParagraph() const
2545 return inset_owner->paragraph();
2547 return bv_owner->buffer()->paragraph;
2551 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2554 inset_owner->paragraph(p);
2556 bv_owner->buffer()->paragraph = p;
2561 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2563 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2564 if (op && op->inInset()) {
2565 static_cast<InsetText *>(op->inInset())->paragraph(p);
2568 inset_owner->paragraph(p);
2570 bv_owner->buffer()->paragraph = p;
2577 LyXText::text_status LyXText::status() const
2583 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2585 // well as much as I know && binds more then || so the above and the
2586 // below are identical (this for your known use of parentesis!)
2587 // Now some explanation:
2588 // We should only go up with refreshing code so this means that if
2589 // we have a MORE refresh we should never set it to LITTLE if we still
2590 // didn't handle it (and then it will be UNCHANGED. Now as long as
2591 // we stay inside one LyXText this may work but we need to tell the
2592 // outermost LyXText that it should REALLY draw us if there is some
2593 // change in a Inset::LyXText. So you see that when we are inside a
2594 // inset's LyXText we give the LITTLE to the outermost LyXText to
2595 // tell'em that it should redraw the actual row (where the inset
2596 // resides! Capito?!
2598 if ((status_ != NEED_MORE_REFRESH)
2599 || (status_ == NEED_MORE_REFRESH
2600 && st != NEED_VERY_LITTLE_REFRESH))
2603 if (inset_owner && st != UNCHANGED) {
2604 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2605 if (!bview->text->refresh_row) {
2606 bview->text->refresh_row = bview->text->cursor.row();
2607 bview->text->refresh_y = bview->text->cursor.y() -
2608 bview->text->cursor.row()->baseline();