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,
1019 if (!selection.set()) return string();
1022 // Special handling if the whole selection is within one paragraph
1023 if (selection.start.par() == selection.end.par()) {
1024 result += selection.start.par()->asString(buffer,
1025 selection.start.pos(),
1026 selection.end.pos(),
1031 // The selection spans more than one paragraph
1033 // First paragraph in selection
1034 result += selection.start.par()->asString(buffer,
1035 selection.start.pos(),
1036 selection.start.par()->size(),
1040 // The paragraphs in between (if any)
1041 LyXCursor tmpcur(selection.start);
1042 tmpcur.par(tmpcur.par()->next());
1043 while (tmpcur.par() != selection.end.par()) {
1044 result += tmpcur.par()->asString(buffer, 0,
1045 tmpcur.par()->size(),
1047 tmpcur.par(tmpcur.par()->next());
1050 // Last paragraph in selection
1051 result += selection.end.par()->asString(buffer, 0,
1052 selection.end.pos(), label);
1058 void LyXText::clearSelection() const
1060 selection.set(false);
1061 selection.mark(false);
1062 selection.end = selection.start = selection.cursor = cursor;
1066 void LyXText::cursorHome(BufferView * bview) const
1068 setCursor(bview, cursor.par(), cursor.row()->pos());
1072 void LyXText::cursorEnd(BufferView * bview) const
1074 if (!cursor.row()->next()
1075 || cursor.row()->next()->par() != cursor.row()->par()) {
1076 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1078 if (cursor.par()->size() &&
1079 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1080 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1081 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1083 setCursor(bview,cursor.par(),
1084 rowLast(cursor.row()) + 1);
1090 void LyXText::cursorTop(BufferView * bview) const
1092 while (cursor.par()->previous())
1093 cursor.par(cursor.par()->previous());
1094 setCursor(bview, cursor.par(), 0);
1098 void LyXText::cursorBottom(BufferView * bview) const
1100 while (cursor.par()->next())
1101 cursor.par(cursor.par()->next());
1102 setCursor(bview, cursor.par(), cursor.par()->size());
1106 void LyXText::toggleFree(BufferView * bview,
1107 LyXFont const & font, bool toggleall)
1109 // If the mask is completely neutral, tell user
1110 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1111 // Could only happen with user style
1112 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1116 // Try implicit word selection
1117 // If there is a change in the language the implicit word selection
1119 LyXCursor resetCursor = cursor;
1120 bool implicitSelection = (font.language() == ignore_language
1121 && font.number() == LyXFont::IGNORE)
1122 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1125 setFont(bview, font, toggleall);
1127 // Implicit selections are cleared afterwards
1128 //and cursor is set to the original position.
1129 if (implicitSelection) {
1131 cursor = resetCursor;
1132 setCursor(bview, cursor.par(), cursor.pos());
1133 selection.cursor = cursor;
1136 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1140 string LyXText::getStringToIndex(BufferView * bview)
1144 // Try implicit word selection
1145 // If there is a change in the language the implicit word selection
1147 LyXCursor resetCursor = cursor;
1148 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1150 if (!selection.set()) {
1151 bview->owner()->message(_("Nothing to index!"));
1154 if (selection.start.par() != selection.end.par()) {
1155 bview->owner()->message(_("Cannot index more than one paragraph!"));
1159 idxstring = selectionAsString(bview->buffer(), false);
1161 // Implicit selections are cleared afterwards
1162 //and cursor is set to the original position.
1163 if (implicitSelection) {
1165 cursor = resetCursor;
1166 setCursor(bview, cursor.par(), cursor.pos());
1167 selection.cursor = cursor;
1173 Paragraph::size_type LyXText::beginningOfMainBody(Buffer const * buf,
1174 Paragraph const * par) const
1176 if (textclasslist.Style(buf->params.textclass,
1177 par->getLayout()).labeltype != LABEL_MANUAL)
1180 return par->beginningOfMainBody();
1184 /* the DTP switches for paragraphs. LyX will store them in the
1185 * first physicla paragraph. When a paragraph is broken, the top settings
1186 * rest, the bottom settings are given to the new one. So I can make shure,
1187 * they do not duplicate themself and you cannnot make dirty things with
1190 void LyXText::setParagraph(BufferView * bview,
1191 bool line_top, bool line_bottom,
1192 bool pagebreak_top, bool pagebreak_bottom,
1193 VSpace const & space_top,
1194 VSpace const & space_bottom,
1195 Spacing const & spacing,
1197 string labelwidthstring,
1200 LyXCursor tmpcursor = cursor;
1201 if (!selection.set()) {
1202 selection.start = cursor;
1203 selection.end = cursor;
1206 // make sure that the depth behind the selection are restored, too
1207 Paragraph * endpar = selection.end.par()->next();
1208 Paragraph * undoendpar = endpar;
1210 if (endpar && endpar->getDepth()) {
1211 while (endpar && endpar->getDepth()) {
1212 endpar = endpar->next();
1213 undoendpar = endpar;
1217 // because of parindents etc.
1218 endpar = endpar->next();
1221 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1224 Paragraph * tmppar = selection.end.par();
1225 while (tmppar != selection.start.par()->previous()) {
1226 setCursor(bview, tmppar, 0);
1227 status(bview, LyXText::NEED_MORE_REFRESH);
1228 refresh_row = cursor.row();
1229 refresh_y = cursor.y() - cursor.row()->baseline();
1230 cursor.par()->params().lineTop(line_top);
1231 cursor.par()->params().lineBottom(line_bottom);
1232 cursor.par()->params().pagebreakTop(pagebreak_top);
1233 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1234 cursor.par()->params().spaceTop(space_top);
1235 cursor.par()->params().spaceBottom(space_bottom);
1236 cursor.par()->params().spacing(spacing);
1237 // does the layout allow the new alignment?
1238 if (align == LYX_ALIGN_LAYOUT)
1239 align = textclasslist
1240 .Style(bview->buffer()->params.textclass,
1241 cursor.par()->getLayout()).align;
1242 if (align & textclasslist
1243 .Style(bview->buffer()->params.textclass,
1244 cursor.par()->getLayout()).alignpossible) {
1245 if (align == textclasslist
1246 .Style(bview->buffer()->params.textclass,
1247 cursor.par()->getLayout()).align)
1248 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1250 cursor.par()->params().align(align);
1252 cursor.par()->setLabelWidthString(labelwidthstring);
1253 cursor.par()->params().noindent(noindent);
1254 tmppar = cursor.par()->previous();
1257 redoParagraphs(bview, selection.start, endpar);
1260 setCursor(bview, selection.start.par(), selection.start.pos());
1261 selection.cursor = cursor;
1262 setCursor(bview, selection.end.par(), selection.end.pos());
1263 setSelection(bview);
1264 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1266 bview->updateInset(inset_owner, true);
1270 char loweralphaCounter(int n)
1272 if (n < 1 || n > 26)
1282 char alphaCounter(int n)
1284 if (n < 1 || n > 26)
1292 char hebrewCounter(int n)
1294 static const char hebrew[22] = {
1295 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1296 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1297 '÷', 'ø', 'ù', 'ú'
1299 if (n < 1 || n > 22)
1307 string const romanCounter(int n)
1309 static char const * roman[20] = {
1310 "i", "ii", "iii", "iv", "v",
1311 "vi", "vii", "viii", "ix", "x",
1312 "xi", "xii", "xiii", "xiv", "xv",
1313 "xvi", "xvii", "xviii", "xix", "xx"
1315 if (n < 1 || n > 20)
1324 // set the counter of a paragraph. This includes the labels
1325 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1327 LyXLayout const & layout =
1328 textclasslist.Style(buf->params.textclass,
1331 LyXTextClass const & textclass =
1332 textclasslist.TextClass(buf->params.textclass);
1334 // copy the prev-counters to this one,
1335 // unless this is the first paragraph
1336 if (par->previous()) {
1337 for (int i = 0; i < 10; ++i) {
1338 par->setCounter(i, par->previous()->getFirstCounter(i));
1340 par->params().appendix(par->previous()->params().appendix());
1341 if (!par->params().appendix() && par->params().startOfAppendix()) {
1342 par->params().appendix(true);
1343 for (int i = 0; i < 10; ++i) {
1344 par->setCounter(i, 0);
1347 par->enumdepth = par->previous()->enumdepth;
1348 par->itemdepth = par->previous()->itemdepth;
1350 for (int i = 0; i < 10; ++i) {
1351 par->setCounter(i, 0);
1353 par->params().appendix(par->params().startOfAppendix());
1358 /* Maybe we have to increment the enumeration depth.
1359 * BUT, enumeration in a footnote is considered in isolation from its
1360 * surrounding paragraph so don't increment if this is the
1361 * first line of the footnote
1362 * AND, bibliographies can't have their depth changed ie. they
1363 * are always of depth 0
1366 && par->previous()->getDepth() < par->getDepth()
1367 && textclasslist.Style(buf->params.textclass,
1368 par->previous()->getLayout()
1369 ).labeltype == LABEL_COUNTER_ENUMI
1370 && par->enumdepth < 3
1371 && layout.labeltype != LABEL_BIBLIO) {
1375 // Maybe we have to decrement the enumeration depth, see note above
1377 && par->previous()->getDepth() > par->getDepth()
1378 && layout.labeltype != LABEL_BIBLIO) {
1379 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1380 par->setCounter(6 + par->enumdepth,
1381 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1382 /* reset the counters.
1383 * A depth change is like a breaking layout
1385 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1386 par->setCounter(i, 0);
1389 if (!par->params().labelString().empty()) {
1390 par->params().labelString(string());
1393 if (layout.margintype == MARGIN_MANUAL) {
1394 if (par->params().labelWidthString().empty()) {
1395 par->setLabelWidthString(layout.labelstring());
1398 par->setLabelWidthString(string());
1401 // is it a layout that has an automatic label?
1402 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1404 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1405 if (i >= 0 && i<= buf->params.secnumdepth) {
1406 par->incCounter(i); // increment the counter
1408 // Is there a label? Useful for Chapter layout
1409 if (!par->params().appendix()) {
1410 if (!layout.labelstring().empty())
1411 par->params().labelString(layout.labelstring());
1413 par->params().labelString(string());
1415 if (!layout.labelstring_appendix().empty())
1416 par->params().labelString(layout.labelstring_appendix());
1418 par->params().labelString(string());
1423 if (!par->params().appendix()) {
1424 switch (2 * LABEL_COUNTER_CHAPTER -
1425 textclass.maxcounter() + i) {
1426 case LABEL_COUNTER_CHAPTER:
1427 s << par->getCounter(i);
1429 case LABEL_COUNTER_SECTION:
1430 s << par->getCounter(i - 1) << '.'
1431 << par->getCounter(i);
1433 case LABEL_COUNTER_SUBSECTION:
1434 s << par->getCounter(i - 2) << '.'
1435 << par->getCounter(i - 1) << '.'
1436 << par->getCounter(i);
1438 case LABEL_COUNTER_SUBSUBSECTION:
1439 s << par->getCounter(i - 3) << '.'
1440 << par->getCounter(i - 2) << '.'
1441 << par->getCounter(i - 1) << '.'
1442 << par->getCounter(i);
1445 case LABEL_COUNTER_PARAGRAPH:
1446 s << par->getCounter(i - 4) << '.'
1447 << par->getCounter(i - 3) << '.'
1448 << par->getCounter(i - 2) << '.'
1449 << par->getCounter(i - 1) << '.'
1450 << par->getCounter(i);
1452 case LABEL_COUNTER_SUBPARAGRAPH:
1453 s << par->getCounter(i - 5) << '.'
1454 << par->getCounter(i - 4) << '.'
1455 << par->getCounter(i - 3) << '.'
1456 << par->getCounter(i - 2) << '.'
1457 << par->getCounter(i - 1) << '.'
1458 << par->getCounter(i);
1462 // Can this ever be reached? And in the
1463 // case it is, how can this be correct?
1465 s << par->getCounter(i) << '.';
1468 } else { // appendix
1469 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1470 case LABEL_COUNTER_CHAPTER:
1471 if (par->isRightToLeftPar(buf->params))
1472 s << hebrewCounter(par->getCounter(i));
1474 s << alphaCounter(par->getCounter(i));
1476 case LABEL_COUNTER_SECTION:
1477 if (par->isRightToLeftPar(buf->params))
1478 s << hebrewCounter(par->getCounter(i - 1));
1480 s << alphaCounter(par->getCounter(i - 1));
1483 << par->getCounter(i);
1486 case LABEL_COUNTER_SUBSECTION:
1487 if (par->isRightToLeftPar(buf->params))
1488 s << hebrewCounter(par->getCounter(i - 2));
1490 s << alphaCounter(par->getCounter(i - 2));
1493 << par->getCounter(i-1) << '.'
1494 << par->getCounter(i);
1497 case LABEL_COUNTER_SUBSUBSECTION:
1498 if (par->isRightToLeftPar(buf->params))
1499 s << hebrewCounter(par->getCounter(i-3));
1501 s << alphaCounter(par->getCounter(i-3));
1504 << par->getCounter(i-2) << '.'
1505 << par->getCounter(i-1) << '.'
1506 << par->getCounter(i);
1509 case LABEL_COUNTER_PARAGRAPH:
1510 if (par->isRightToLeftPar(buf->params))
1511 s << hebrewCounter(par->getCounter(i-4));
1513 s << alphaCounter(par->getCounter(i-4));
1516 << par->getCounter(i-3) << '.'
1517 << par->getCounter(i-2) << '.'
1518 << par->getCounter(i-1) << '.'
1519 << par->getCounter(i);
1522 case LABEL_COUNTER_SUBPARAGRAPH:
1523 if (par->isRightToLeftPar(buf->params))
1524 s << hebrewCounter(par->getCounter(i-5));
1526 s << alphaCounter(par->getCounter(i-5));
1529 << par->getCounter(i-4) << '.'
1530 << par->getCounter(i-3) << '.'
1531 << par->getCounter(i-2) << '.'
1532 << par->getCounter(i-1) << '.'
1533 << par->getCounter(i);
1537 // Can this ever be reached? And in the
1538 // case it is, how can this be correct?
1540 s << par->getCounter(i) << '.';
1546 par->params().labelString(par->params().labelString() +s.str().c_str());
1547 // We really want to remove the c_str as soon as
1550 for (i++; i < 10; ++i) {
1551 // reset the following counters
1552 par->setCounter(i, 0);
1554 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1555 for (i++; i < 10; ++i) {
1556 // reset the following counters
1557 par->setCounter(i, 0);
1559 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1560 par->incCounter(i + par->enumdepth);
1561 int number = par->getCounter(i + par->enumdepth);
1565 switch (par->enumdepth) {
1567 if (par->isRightToLeftPar(buf->params))
1569 << hebrewCounter(number)
1573 << loweralphaCounter(number)
1577 if (par->isRightToLeftPar(buf->params))
1578 s << '.' << romanCounter(number);
1580 s << romanCounter(number) << '.';
1583 if (par->isRightToLeftPar(buf->params))
1585 << alphaCounter(number);
1587 s << alphaCounter(number)
1591 if (par->isRightToLeftPar(buf->params))
1598 par->params().labelString(s.str().c_str());
1600 for (i += par->enumdepth + 1; i < 10; ++i) {
1601 // reset the following counters
1602 par->setCounter(i, 0);
1606 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1607 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1609 int number = par->getCounter(i);
1611 InsetCommandParams p( "bibitem" );
1612 par->bibkey = new InsetBibKey(p);
1614 par->bibkey->setCounter(number);
1615 par->params().labelString(layout.labelstring());
1617 // In biblio should't be following counters but...
1619 string s = layout.labelstring();
1621 // the caption hack:
1622 if (layout.labeltype == LABEL_SENSITIVE) {
1623 bool isOK (par->inInset() && par->inInset()->owner() &&
1624 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1627 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1629 = floatList.getType(tmp->type());
1630 // We should get the correct number here too.
1631 s = fl.name() + " #:";
1633 /* par->SetLayout(0);
1634 s = layout->labelstring; */
1635 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1636 ? " :úåòîùî øñç" : "Senseless: ";
1639 par->params().labelString(s);
1641 /* reset the enumeration counter. They are always resetted
1642 * when there is any other layout between */
1643 for (int i = 6 + par->enumdepth; i < 10; ++i)
1644 par->setCounter(i, 0);
1649 // Updates all counters BEHIND the row. Changed paragraphs
1650 // with a dynamic left margin will be rebroken.
1651 void LyXText::updateCounters(BufferView * bview, Row * row) const
1659 par = row->par()->next();
1663 while (row->par() != par)
1666 setCounter(bview->buffer(), par);
1668 // now check for the headline layouts. remember that they
1669 // have a dynamic left margin
1670 if ((textclasslist.Style(bview->buffer()->params.textclass,
1671 par->layout).margintype == MARGIN_DYNAMIC
1672 || textclasslist.Style(bview->buffer()->params.textclass,
1673 par->layout).labeltype == LABEL_SENSITIVE)) {
1675 // Rebreak the paragraph
1676 removeParagraph(row);
1677 appendParagraph(bview, row);
1684 void LyXText::insertInset(BufferView * bview, Inset * inset)
1686 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1688 setUndo(bview, Undo::INSERT,
1689 cursor.par(), cursor.par()->next());
1690 cursor.par()->insertInset(cursor.pos(), inset);
1691 // Just to rebreak and refresh correctly.
1692 // The character will not be inserted a second time
1693 insertChar(bview, Paragraph::META_INSET);
1695 // If we enter a highly editable inset the cursor should be to before
1696 // the inset. This couldn't happen before as Undo was not handled inside
1697 // inset now after the Undo LyX tries to call inset->Edit(...) again
1698 // and cannot do this as the cursor is behind the inset and GetInset
1699 // does not return the inset!
1700 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1701 cursorLeft(bview, true);
1707 void LyXText::copyEnvironmentType()
1709 copylayouttype = cursor.par()->getLayout();
1713 void LyXText::pasteEnvironmentType(BufferView * bview)
1715 setLayout(bview, copylayouttype);
1719 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1721 // Stuff what we got on the clipboard. Even if there is no selection.
1723 // There is a problem with having the stuffing here in that the
1724 // larger the selection the slower LyX will get. This can be
1725 // solved by running the line below only when the selection has
1726 // finished. The solution used currently just works, to make it
1727 // faster we need to be more clever and probably also have more
1728 // calls to stuffClipboard. (Lgb)
1729 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1731 // This doesn't make sense, if there is no selection
1732 if (!selection.set())
1735 // OK, we have a selection. This is always between selection.start
1736 // and selection.end
1738 // make sure that the depth behind the selection are restored, too
1739 Paragraph * endpar = selection.end.par()->next();
1740 Paragraph * undoendpar = endpar;
1742 if (endpar && endpar->getDepth()) {
1743 while (endpar && endpar->getDepth()) {
1744 endpar = endpar->next();
1745 undoendpar = endpar;
1747 } else if (endpar) {
1748 endpar = endpar->next(); // because of parindents etc.
1751 setUndo(bview, Undo::DELETE,
1752 selection.start.par(), undoendpar);
1754 // there are two cases: cut only within one paragraph or
1755 // more than one paragraph
1756 if (selection.start.par() == selection.end.par()) {
1757 // only within one paragraph
1758 endpar = selection.end.par();
1759 int pos = selection.end.pos();
1760 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1761 selection.start.pos(), pos,
1762 bview->buffer()->params.textclass,
1764 selection.end.pos(pos);
1766 endpar = selection.end.par();
1767 int pos = selection.end.pos();
1768 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1769 selection.start.pos(), pos,
1770 bview->buffer()->params.textclass,
1773 selection.end.par(endpar);
1774 selection.end.pos(pos);
1775 cursor.pos(selection.end.pos());
1777 endpar = endpar->next();
1779 // sometimes necessary
1781 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1783 redoParagraphs(bview, selection.start, endpar);
1785 // cutSelection can invalidate the cursor so we need to set
1787 cursor = selection.start;
1789 // need a valid cursor. (Lgb)
1792 setCursor(bview, cursor.par(), cursor.pos());
1793 selection.cursor = cursor;
1794 updateCounters(bview, cursor.row());
1798 void LyXText::copySelection(BufferView * bview)
1800 // Stuff what we got on the clipboard. Even if there is no selection.
1802 // There is a problem with having the stuffing here in that the
1803 // larger the selection the slower LyX will get. This can be
1804 // solved by running the line below only when the selection has
1805 // finished. The solution used currently just works, to make it
1806 // faster we need to be more clever and probably also have more
1807 // calls to stuffClipboard. (Lgb)
1808 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1810 // this doesnt make sense, if there is no selection
1811 if (!selection.set())
1814 // ok we have a selection. This is always between selection.start
1815 // and sel_end cursor
1817 // copy behind a space if there is one
1818 while (selection.start.par()->size() > selection.start.pos()
1819 && selection.start.par()->isLineSeparator(selection.start.pos())
1820 && (selection.start.par() != selection.end.par()
1821 || selection.start.pos() < selection.end.pos()))
1822 selection.start.pos(selection.start.pos() + 1);
1824 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1825 selection.start.pos(), selection.end.pos(),
1826 bview->buffer()->params.textclass);
1830 void LyXText::pasteSelection(BufferView * bview)
1832 // this does not make sense, if there is nothing to paste
1833 if (!CutAndPaste::checkPastePossible(cursor.par()))
1836 setUndo(bview, Undo::INSERT,
1837 cursor.par(), cursor.par()->next());
1840 Paragraph * actpar = cursor.par();
1841 int pos = cursor.pos();
1843 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1844 bview->buffer()->params.textclass);
1846 redoParagraphs(bview, cursor, endpar);
1848 setCursor(bview, cursor.par(), cursor.pos());
1851 setCursor(bview, actpar, pos);
1852 updateCounters(bview, cursor.row());
1856 // returns a pointer to the very first Paragraph
1857 Paragraph * LyXText::firstParagraph() const
1859 return ownerParagraph();
1863 // sets the selection over the number of characters of string, no check!!
1864 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1869 selection.cursor = cursor;
1870 for (string::size_type i = 0; i < str.length(); ++i)
1872 setSelection(bview);
1876 // simple replacing. The font of the first selected character is used
1877 void LyXText::replaceSelectionWithString(BufferView * bview,
1880 setCursorParUndo(bview);
1883 if (!selection.set()) { // create a dummy selection
1884 selection.end = cursor;
1885 selection.start = cursor;
1888 // Get font setting before we cut
1889 Paragraph::size_type pos = selection.end.pos();
1890 LyXFont const font = selection.start.par()
1891 ->getFontSettings(bview->buffer()->params,
1892 selection.start.pos());
1894 // Insert the new string
1895 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1896 selection.end.par()->insertChar(pos, (*cit), font);
1900 // Cut the selection
1901 cutSelection(bview, true, false);
1907 // needed to insert the selection
1908 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1910 Paragraph * par = cursor.par();
1911 Paragraph::size_type pos = cursor.pos();
1912 Paragraph * endpar = cursor.par()->next();
1914 setCursorParUndo(bview);
1916 // only to be sure, should not be neccessary
1919 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1921 redoParagraphs(bview, cursor, endpar);
1922 setCursor(bview, cursor.par(), cursor.pos());
1923 selection.cursor = cursor;
1924 setCursor(bview, par, pos);
1925 setSelection(bview);
1929 // turns double-CR to single CR, others where converted into one
1930 // blank. Then InsertStringAsLines is called
1931 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1933 string linestr(str);
1934 bool newline_inserted = false;
1935 for (string::size_type i = 0; i < linestr.length(); ++i) {
1936 if (linestr[i] == '\n') {
1937 if (newline_inserted) {
1938 // we know that \r will be ignored by
1939 // InsertStringA. Of course, it is a dirty
1940 // trick, but it works...
1941 linestr[i - 1] = '\r';
1945 newline_inserted = true;
1947 } else if (IsPrintable(linestr[i])) {
1948 newline_inserted = false;
1951 insertStringAsLines(bview, linestr);
1955 bool LyXText::gotoNextInset(BufferView * bview,
1956 std::vector<Inset::Code> const & codes,
1957 string const & contents) const
1959 LyXCursor res = cursor;
1962 if (res.pos() < res.par()->size() - 1) {
1963 res.pos(res.pos() + 1);
1965 res.par(res.par()->next());
1969 } while (res.par() &&
1970 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1971 && (inset = res.par()->getInset(res.pos())) != 0
1972 && find(codes.begin(), codes.end(), inset->lyxCode())
1974 && (contents.empty() ||
1975 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1979 setCursor(bview, res.par(), res.pos());
1986 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1987 Paragraph::size_type pos)
1989 LyXCursor tmpcursor;
1992 Paragraph::size_type z;
1993 Row * row = getRow(par, pos, y);
1995 // is there a break one row above
1996 if (row->previous() && row->previous()->par() == row->par()) {
1997 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1998 if (z >= row->pos()) {
1999 // set the dimensions of the row above
2000 y -= row->previous()->height();
2002 refresh_row = row->previous();
2003 status(bview, LyXText::NEED_MORE_REFRESH);
2005 breakAgain(bview, row->previous());
2007 // set the cursor again. Otherwise
2008 // dangling pointers are possible
2009 setCursor(bview, cursor.par(), cursor.pos(),
2010 false, cursor.boundary());
2011 selection.cursor = cursor;
2016 int const tmpheight = row->height();
2017 Paragraph::size_type const tmplast = rowLast(row);
2021 breakAgain(bview, row);
2022 if (row->height() == tmpheight && rowLast(row) == tmplast)
2023 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2025 status(bview, LyXText::NEED_MORE_REFRESH);
2027 // check the special right address boxes
2028 if (textclasslist.Style(bview->buffer()->params.textclass,
2029 par->getLayout()).margintype
2030 == MARGIN_RIGHT_ADDRESS_BOX)
2038 redoDrawingOfParagraph(bview, tmpcursor);
2041 // set the cursor again. Otherwise dangling pointers are possible
2042 // also set the selection
2044 if (selection.set()) {
2046 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2047 false, selection.cursor.boundary());
2048 selection.cursor = cursor;
2049 setCursorIntern(bview, selection.start.par(),
2050 selection.start.pos(),
2051 false, selection.start.boundary());
2052 selection.start = cursor;
2053 setCursorIntern(bview, selection.end.par(),
2054 selection.end.pos(),
2055 false, selection.end.boundary());
2056 selection.end = cursor;
2057 setCursorIntern(bview, last_sel_cursor.par(),
2058 last_sel_cursor.pos(),
2059 false, last_sel_cursor.boundary());
2060 last_sel_cursor = cursor;
2063 setCursorIntern(bview, cursor.par(), cursor.pos(),
2064 false, cursor.boundary());
2068 // returns false if inset wasn't found
2069 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2071 // first check the current paragraph
2072 int pos = cursor.par()->getPositionOfInset(inset);
2074 checkParagraph(bview, cursor.par(), pos);
2078 // check every paragraph
2080 Paragraph * par = firstParagraph();
2082 pos = par->getPositionOfInset(inset);
2084 checkParagraph(bview, par, pos);
2094 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2095 Paragraph::size_type pos,
2096 bool setfont, bool boundary) const
2098 LyXCursor old_cursor = cursor;
2099 setCursorIntern(bview, par, pos, setfont, boundary);
2100 deleteEmptyParagraphMechanism(bview, old_cursor);
2104 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2105 Paragraph::size_type pos, bool boundary) const
2109 cur.boundary(boundary);
2111 // get the cursor y position in text
2113 Row * row = getRow(par, pos, y);
2114 // y is now the beginning of the cursor row
2115 y += row->baseline();
2116 // y is now the cursor baseline
2119 // now get the cursors x position
2121 float fill_separator;
2123 float fill_label_hfill;
2124 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2126 Paragraph::size_type cursor_vpos = 0;
2127 Paragraph::size_type last = rowLastPrintable(row);
2129 if (pos > last + 1) // This shouldn't happen.
2131 else if (pos < row->pos())
2134 if (last < row->pos())
2135 cursor_vpos = row->pos();
2136 else if (pos > last && !boundary)
2137 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2138 ? row->pos() : last + 1;
2139 else if (pos > row->pos() &&
2140 (pos > last || boundary))
2141 /// Place cursor after char at (logical) position pos - 1
2142 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2143 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2145 /// Place cursor before char at (logical) position pos
2146 cursor_vpos = (bidi_level(pos) % 2 == 0)
2147 ? log2vis(pos) : log2vis(pos) + 1;
2149 Paragraph::size_type main_body =
2150 beginningOfMainBody(bview->buffer(), row->par());
2151 if ((main_body > 0) &&
2152 ((main_body-1 > last) ||
2153 !row->par()->isLineSeparator(main_body-1)))
2156 for (Paragraph::size_type vpos = row->pos();
2157 vpos < cursor_vpos; ++vpos) {
2158 pos = vis2log(vpos);
2159 if (main_body > 0 && pos == main_body - 1) {
2160 x += fill_label_hfill +
2161 lyxfont::width(textclasslist.Style(
2162 bview->buffer()->params.textclass,
2163 row->par()->getLayout())
2165 getLabelFont(bview->buffer(), row->par()));
2166 if (row->par()->isLineSeparator(main_body-1))
2167 x -= singleWidth(bview, row->par(),main_body-1);
2169 if (hfillExpansion(bview->buffer(), row, pos)) {
2170 x += singleWidth(bview, row->par(), pos);
2171 if (pos >= main_body)
2174 x += fill_label_hfill;
2175 } else if (row->par()->isSeparator(pos)) {
2176 x += singleWidth(bview, row->par(), pos);
2177 if (pos >= main_body)
2178 x += fill_separator;
2180 x += singleWidth(bview, row->par(), pos);
2189 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2190 Paragraph::size_type pos,
2191 bool setfont, bool boundary) const
2193 InsetText * it = static_cast<InsetText *>(par->inInset());
2195 if (it != inset_owner) {
2196 lyxerr << "InsetText is " << it << endl;
2197 lyxerr << "inset_owner is " << inset_owner << endl;
2198 #warning I believe this code is wrong. (Lgb)
2199 #warning Jürgen, have a look at this. (Lgb)
2200 #warning Hmmm, I guess you are right but we
2201 #warning should verify when this is needed
2202 // Jürgen, would you like to have a look?
2203 // I guess we need to move the outer cursor
2204 // and open and lock the inset (bla bla bla)
2205 // stuff I don't know... so can you have a look?
2207 // I moved the lyxerr stuff in here so we can see if
2208 // this is actually really needed and where!
2210 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2216 setCursor(bview, cursor, par, pos, boundary);
2218 setCurrentFont(bview);
2222 void LyXText::setCurrentFont(BufferView * bview) const
2224 Paragraph::size_type pos = cursor.pos();
2225 if (cursor.boundary() && pos > 0)
2229 if (pos == cursor.par()->size())
2231 else // potentional bug... BUG (Lgb)
2232 if (cursor.par()->isSeparator(pos)) {
2233 if (pos > cursor.row()->pos() &&
2234 bidi_level(pos) % 2 ==
2235 bidi_level(pos - 1) % 2)
2237 else if (pos + 1 < cursor.par()->size())
2243 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2244 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2246 if (cursor.pos() == cursor.par()->size() &&
2247 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2248 !cursor.boundary()) {
2249 Language const * lang =
2250 cursor.par()->getParLanguage(bview->buffer()->params);
2251 current_font.setLanguage(lang);
2252 current_font.setNumber(LyXFont::OFF);
2253 real_current_font.setLanguage(lang);
2254 real_current_font.setNumber(LyXFont::OFF);
2259 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2261 LyXCursor old_cursor = cursor;
2263 setCursorFromCoordinates(bview, cursor, x, y);
2264 setCurrentFont(bview);
2265 deleteEmptyParagraphMechanism(bview, old_cursor);
2269 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2272 // Get the row first.
2274 Row * row = getRowNearY(y);
2276 Paragraph::size_type const column = getColumnNearX(bview, row, x,
2279 cur.par(row->par());
2280 cur.pos(row->pos() + column);
2282 cur.y(y + row->baseline());
2284 cur.boundary(bound);
2288 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2290 if (cursor.pos() > 0) {
2291 bool boundary = cursor.boundary();
2292 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2293 if (!internal && !boundary &&
2294 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2295 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2296 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2297 Paragraph * par = cursor.par()->previous();
2298 setCursor(bview, par, par->size());
2303 void LyXText::cursorRight(BufferView * bview, bool internal) const
2305 if (!internal && cursor.boundary() &&
2306 !cursor.par()->isNewline(cursor.pos()))
2307 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2308 else if (cursor.pos() < cursor.par()->size()) {
2309 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2311 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2312 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2313 } else if (cursor.par()->next())
2314 setCursor(bview, cursor.par()->next(), 0);
2318 void LyXText::cursorUp(BufferView * bview) const
2320 setCursorFromCoordinates(bview, cursor.x_fix(),
2321 cursor.y() - cursor.row()->baseline() - 1);
2325 void LyXText::cursorDown(BufferView * bview) const
2327 setCursorFromCoordinates(bview, cursor.x_fix(),
2328 cursor.y() - cursor.row()->baseline()
2329 + cursor.row()->height() + 1);
2333 void LyXText::cursorUpParagraph(BufferView * bview) const
2335 if (cursor.pos() > 0) {
2336 setCursor(bview, cursor.par(), 0);
2338 else if (cursor.par()->previous()) {
2339 setCursor(bview, cursor.par()->previous(), 0);
2344 void LyXText::cursorDownParagraph(BufferView * bview) const
2346 if (cursor.par()->next()) {
2347 setCursor(bview, cursor.par()->next(), 0);
2349 setCursor(bview, cursor.par(), cursor.par()->size());
2353 // fix the cursor `cur' after a characters has been deleted at `where'
2354 // position. Called by deleteEmptyParagraphMechanism
2355 void LyXText::fixCursorAfterDelete(BufferView * bview,
2357 LyXCursor const & where) const
2359 // if cursor is not in the paragraph where the delete occured,
2361 if (cur.par() != where.par())
2364 // if cursor position is after the place where the delete occured,
2366 if (cur.pos() > where.pos())
2367 cur.pos(cur.pos()-1);
2369 // recompute row et al. for this cursor
2370 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2374 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2375 LyXCursor const & old_cursor) const
2377 // Would be wrong to delete anything if we have a selection.
2378 if (selection.set()) return;
2380 // We allow all kinds of "mumbo-jumbo" when freespacing.
2381 if (textclasslist.Style(bview->buffer()->params.textclass,
2382 old_cursor.par()->getLayout()).free_spacing)
2385 bool deleted = false;
2387 /* Ok I'll put some comments here about what is missing.
2388 I have fixed BackSpace (and thus Delete) to not delete
2389 double-spaces automagically. I have also changed Cut,
2390 Copy and Paste to hopefully do some sensible things.
2391 There are still some small problems that can lead to
2392 double spaces stored in the document file or space at
2393 the beginning of paragraphs. This happens if you have
2394 the cursor betwenn to spaces and then save. Or if you
2395 cut and paste and the selection have a space at the
2396 beginning and then save right after the paste. I am
2397 sure none of these are very hard to fix, but I will
2398 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2399 that I can get some feedback. (Lgb)
2402 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2403 // delete the LineSeparator.
2406 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2407 // delete the LineSeparator.
2410 // If the pos around the old_cursor were spaces, delete one of them.
2411 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2412 // Only if the cursor has really moved
2414 if (old_cursor.pos() > 0
2415 && old_cursor.pos() < old_cursor.par()->size()
2416 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2417 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2418 old_cursor.par()->erase(old_cursor.pos() - 1);
2419 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2421 #ifdef WITH_WARNINGS
2422 #warning This will not work anymore when we have multiple views of the same buffer
2423 // In this case, we will have to correct also the cursors held by
2424 // other bufferviews. It will probably be easier to do that in a more
2425 // automated way in LyXCursor code. (JMarc 26/09/2001)
2427 // correct all cursors held by the LyXText
2428 fixCursorAfterDelete(bview, cursor, old_cursor);
2429 fixCursorAfterDelete(bview, selection.cursor,
2431 fixCursorAfterDelete(bview, selection.start,
2433 fixCursorAfterDelete(bview, selection.end, old_cursor);
2434 fixCursorAfterDelete(bview, last_sel_cursor,
2436 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2437 fixCursorAfterDelete(bview, toggle_end_cursor,
2443 // Do not delete empty paragraphs with keepempty set.
2444 if ((textclasslist.Style(bview->buffer()->params.textclass,
2445 old_cursor.par()->getLayout())).keepempty)
2448 // only do our magic if we changed paragraph
2449 if (old_cursor.par() == cursor.par())
2452 if ((old_cursor.par()->size() == 0
2453 || (old_cursor.par()->size() == 1
2454 && old_cursor.par()->isLineSeparator(0)))) {
2455 // ok, we will delete anything
2456 LyXCursor tmpcursor;
2458 // make sure that you do not delete any environments
2459 status(bview, LyXText::NEED_MORE_REFRESH);
2462 if (old_cursor.row()->previous()) {
2463 refresh_row = old_cursor.row()->previous();
2464 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2466 cursor = old_cursor; // that undo can restore the right cursor position
2467 Paragraph * endpar = old_cursor.par()->next();
2468 if (endpar && endpar->getDepth()) {
2469 while (endpar && endpar->getDepth()) {
2470 endpar = endpar->next();
2473 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2477 removeRow(old_cursor.row());
2478 if (ownerParagraph() == old_cursor.par()) {
2479 ownerParagraph(ownerParagraph()->next());
2482 delete old_cursor.par();
2484 /* Breakagain the next par. Needed because of
2485 * the parindent that can occur or dissappear.
2486 * The next row can change its height, if
2487 * there is another layout before */
2488 if (refresh_row->next()) {
2489 breakAgain(bview, refresh_row->next());
2490 updateCounters(bview, refresh_row);
2492 setHeightOfRow(bview, refresh_row);
2494 refresh_row = old_cursor.row()->next();
2495 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2498 cursor = old_cursor; // that undo can restore the right cursor position
2499 Paragraph * endpar = old_cursor.par()->next();
2500 if (endpar && endpar->getDepth()) {
2501 while (endpar && endpar->getDepth()) {
2502 endpar = endpar->next();
2505 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2509 removeRow(old_cursor.row());
2511 if (ownerParagraph() == old_cursor.par()) {
2512 ownerParagraph(ownerParagraph()->next());
2515 delete old_cursor.par();
2517 /* Breakagain the next par. Needed because of
2518 the parindent that can occur or dissappear.
2519 The next row can change its height, if
2520 there is another layout before */
2522 breakAgain(bview, refresh_row);
2523 updateCounters(bview, refresh_row->previous());
2528 setCursorIntern(bview, cursor.par(), cursor.pos());
2530 if (selection.cursor.par() == old_cursor.par()
2531 && selection.cursor.pos() == selection.cursor.pos()) {
2532 // correct selection
2533 selection.cursor = cursor;
2537 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2538 redoParagraphs(bview, old_cursor,
2539 old_cursor.par()->next());
2541 setCursorIntern(bview, cursor.par(), cursor.pos());
2542 selection.cursor = cursor;
2548 void LyXText::toggleAppendix(BufferView * bview)
2550 Paragraph * par = cursor.par();
2551 bool start = !par->params().startOfAppendix();
2553 // ensure that we have only one start_of_appendix in this document
2554 Paragraph * tmp = firstParagraph();
2555 for (; tmp; tmp = tmp->next()) {
2556 tmp->params().startOfAppendix(false);
2559 par->params().startOfAppendix(start);
2561 // we can set the refreshing parameters now
2562 status(bview, LyXText::NEED_MORE_REFRESH);
2564 refresh_row = 0; // not needed for full update
2565 updateCounters(bview, 0);
2566 setCursor(bview, cursor.par(), cursor.pos());
2570 Paragraph * LyXText::ownerParagraph() const
2573 return inset_owner->paragraph();
2575 return bv_owner->buffer()->paragraph;
2579 void LyXText::ownerParagraph(Paragraph * p) const
2582 inset_owner->paragraph(p);
2584 bv_owner->buffer()->paragraph = p;
2589 void LyXText::ownerParagraph(int id, Paragraph * p) const
2591 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2592 if (op && op->inInset()) {
2593 static_cast<InsetText *>(op->inInset())->paragraph(p);
2596 inset_owner->paragraph(p);
2598 bv_owner->buffer()->paragraph = p;
2604 LyXText::text_status LyXText::status() const
2610 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2612 // well as much as I know && binds more then || so the above and the
2613 // below are identical (this for your known use of parentesis!)
2614 // Now some explanation:
2615 // We should only go up with refreshing code so this means that if
2616 // we have a MORE refresh we should never set it to LITTLE if we still
2617 // didn't handle it (and then it will be UNCHANGED. Now as long as
2618 // we stay inside one LyXText this may work but we need to tell the
2619 // outermost LyXText that it should REALLY draw us if there is some
2620 // change in a Inset::LyXText. So you see that when we are inside a
2621 // inset's LyXText we give the LITTLE to the outermost LyXText to
2622 // tell'em that it should redraw the actual row (where the inset
2623 // resides! Capito?!
2625 if ((status_ != NEED_MORE_REFRESH)
2626 || (status_ == NEED_MORE_REFRESH
2627 && st != NEED_VERY_LITTLE_REFRESH))
2630 if (inset_owner && st != UNCHANGED) {
2631 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2632 if (!bview->text->refresh_row) {
2633 bview->text->refresh_row = bview->text->cursor.row();
2634 bview->text->refresh_y = bview->text->cursor.y() -
2635 bview->text->cursor.row()->baseline();