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);
1141 LyXText::getStringToIndex(BufferView * bview)
1145 // Try implicit word selection
1146 // If there is a change in the language the implicit word selection
1148 LyXCursor resetCursor = cursor;
1149 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1151 if (!selection.set()) {
1152 bview->owner()->message(_("Nothing to index!"));
1155 if (selection.start.par() != selection.end.par()) {
1156 bview->owner()->message(_("Cannot index more than one paragraph!"));
1160 idxstring = selectionAsString(bview->buffer(), false);
1162 // Implicit selections are cleared afterwards
1163 //and cursor is set to the original position.
1164 if (implicitSelection) {
1166 cursor = resetCursor;
1167 setCursor(bview, cursor.par(), cursor.pos());
1168 selection.cursor = cursor;
1173 Paragraph::size_type
1174 LyXText::beginningOfMainBody(Buffer const * buf,
1175 Paragraph const * par) const
1177 if (textclasslist.Style(buf->params.textclass,
1178 par->getLayout()).labeltype != LABEL_MANUAL)
1181 return par->beginningOfMainBody();
1185 /* the DTP switches for paragraphs. LyX will store them in the
1186 * first physicla paragraph. When a paragraph is broken, the top settings
1187 * rest, the bottom settings are given to the new one. So I can make shure,
1188 * they do not duplicate themself and you cannnot make dirty things with
1191 void LyXText::setParagraph(BufferView * bview,
1192 bool line_top, bool line_bottom,
1193 bool pagebreak_top, bool pagebreak_bottom,
1194 VSpace const & space_top,
1195 VSpace const & space_bottom,
1196 Spacing const & spacing,
1198 string labelwidthstring,
1201 LyXCursor tmpcursor = cursor;
1202 if (!selection.set()) {
1203 selection.start = cursor;
1204 selection.end = cursor;
1207 // make sure that the depth behind the selection are restored, too
1208 Paragraph * endpar = selection.end.par()->next();
1209 Paragraph * undoendpar = endpar;
1211 if (endpar && endpar->getDepth()) {
1212 while (endpar && endpar->getDepth()) {
1213 endpar = endpar->next();
1214 undoendpar = endpar;
1218 // because of parindents etc.
1219 endpar = endpar->next();
1222 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1225 Paragraph * tmppar = selection.end.par();
1226 while (tmppar != selection.start.par()->previous()) {
1227 setCursor(bview, tmppar, 0);
1228 status(bview, LyXText::NEED_MORE_REFRESH);
1229 refresh_row = cursor.row();
1230 refresh_y = cursor.y() - cursor.row()->baseline();
1231 cursor.par()->params().lineTop(line_top);
1232 cursor.par()->params().lineBottom(line_bottom);
1233 cursor.par()->params().pagebreakTop(pagebreak_top);
1234 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1235 cursor.par()->params().spaceTop(space_top);
1236 cursor.par()->params().spaceBottom(space_bottom);
1237 cursor.par()->params().spacing(spacing);
1238 // does the layout allow the new alignment?
1239 if (align == LYX_ALIGN_LAYOUT)
1240 align = textclasslist
1241 .Style(bview->buffer()->params.textclass,
1242 cursor.par()->getLayout()).align;
1243 if (align & textclasslist
1244 .Style(bview->buffer()->params.textclass,
1245 cursor.par()->getLayout()).alignpossible) {
1246 if (align == textclasslist
1247 .Style(bview->buffer()->params.textclass,
1248 cursor.par()->getLayout()).align)
1249 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1251 cursor.par()->params().align(align);
1253 cursor.par()->setLabelWidthString(labelwidthstring);
1254 cursor.par()->params().noindent(noindent);
1255 tmppar = cursor.par()->previous();
1258 redoParagraphs(bview, selection.start, endpar);
1261 setCursor(bview, selection.start.par(), selection.start.pos());
1262 selection.cursor = cursor;
1263 setCursor(bview, selection.end.par(), selection.end.pos());
1264 setSelection(bview);
1265 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1267 bview->updateInset(inset_owner, true);
1271 char loweralphaCounter(int n)
1273 if (n < 1 || n > 26)
1283 char alphaCounter(int n)
1285 if (n < 1 || n > 26)
1293 char hebrewCounter(int n)
1295 static const char hebrew[22] = {
1296 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1297 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1298 '÷', 'ø', 'ù', 'ú'
1300 if (n < 1 || n > 22)
1308 string const romanCounter(int n)
1310 static char const * roman[20] = {
1311 "i", "ii", "iii", "iv", "v",
1312 "vi", "vii", "viii", "ix", "x",
1313 "xi", "xii", "xiii", "xiv", "xv",
1314 "xvi", "xvii", "xviii", "xix", "xx"
1316 if (n < 1 || n > 20)
1325 // set the counter of a paragraph. This includes the labels
1326 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1328 LyXLayout const & layout =
1329 textclasslist.Style(buf->params.textclass,
1332 LyXTextClass const & textclass =
1333 textclasslist.TextClass(buf->params.textclass);
1335 // copy the prev-counters to this one,
1336 // unless this is the first paragraph
1337 if (par->previous()) {
1338 for (int i = 0; i < 10; ++i) {
1339 par->setCounter(i, par->previous()->getFirstCounter(i));
1341 par->params().appendix(par->previous()->params().appendix());
1342 if (!par->params().appendix() && par->params().startOfAppendix()) {
1343 par->params().appendix(true);
1344 for (int i = 0; i < 10; ++i) {
1345 par->setCounter(i, 0);
1348 par->enumdepth = par->previous()->enumdepth;
1349 par->itemdepth = par->previous()->itemdepth;
1351 for (int i = 0; i < 10; ++i) {
1352 par->setCounter(i, 0);
1354 par->params().appendix(par->params().startOfAppendix());
1359 /* Maybe we have to increment the enumeration depth.
1360 * BUT, enumeration in a footnote is considered in isolation from its
1361 * surrounding paragraph so don't increment if this is the
1362 * first line of the footnote
1363 * AND, bibliographies can't have their depth changed ie. they
1364 * are always of depth 0
1367 && par->previous()->getDepth() < par->getDepth()
1368 && textclasslist.Style(buf->params.textclass,
1369 par->previous()->getLayout()
1370 ).labeltype == LABEL_COUNTER_ENUMI
1371 && par->enumdepth < 3
1372 && layout.labeltype != LABEL_BIBLIO) {
1376 // Maybe we have to decrement the enumeration depth, see note above
1378 && par->previous()->getDepth() > par->getDepth()
1379 && layout.labeltype != LABEL_BIBLIO) {
1380 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1381 par->setCounter(6 + par->enumdepth,
1382 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1383 /* reset the counters.
1384 * A depth change is like a breaking layout
1386 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1387 par->setCounter(i, 0);
1390 if (!par->params().labelString().empty()) {
1391 par->params().labelString(string());
1394 if (layout.margintype == MARGIN_MANUAL) {
1395 if (par->params().labelWidthString().empty()) {
1396 par->setLabelWidthString(layout.labelstring());
1399 par->setLabelWidthString(string());
1402 // is it a layout that has an automatic label?
1403 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1405 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1406 if (i >= 0 && i<= buf->params.secnumdepth) {
1407 par->incCounter(i); // increment the counter
1409 // Is there a label? Useful for Chapter layout
1410 if (!par->params().appendix()) {
1411 if (!layout.labelstring().empty())
1412 par->params().labelString(layout.labelstring());
1414 par->params().labelString(string());
1416 if (!layout.labelstring_appendix().empty())
1417 par->params().labelString(layout.labelstring_appendix());
1419 par->params().labelString(string());
1424 if (!par->params().appendix()) {
1425 switch (2 * LABEL_COUNTER_CHAPTER -
1426 textclass.maxcounter() + i) {
1427 case LABEL_COUNTER_CHAPTER:
1428 s << par->getCounter(i);
1430 case LABEL_COUNTER_SECTION:
1431 s << par->getCounter(i - 1) << '.'
1432 << par->getCounter(i);
1434 case LABEL_COUNTER_SUBSECTION:
1435 s << par->getCounter(i - 2) << '.'
1436 << par->getCounter(i - 1) << '.'
1437 << par->getCounter(i);
1439 case LABEL_COUNTER_SUBSUBSECTION:
1440 s << par->getCounter(i - 3) << '.'
1441 << par->getCounter(i - 2) << '.'
1442 << par->getCounter(i - 1) << '.'
1443 << par->getCounter(i);
1446 case LABEL_COUNTER_PARAGRAPH:
1447 s << par->getCounter(i - 4) << '.'
1448 << par->getCounter(i - 3) << '.'
1449 << par->getCounter(i - 2) << '.'
1450 << par->getCounter(i - 1) << '.'
1451 << par->getCounter(i);
1453 case LABEL_COUNTER_SUBPARAGRAPH:
1454 s << par->getCounter(i - 5) << '.'
1455 << par->getCounter(i - 4) << '.'
1456 << par->getCounter(i - 3) << '.'
1457 << par->getCounter(i - 2) << '.'
1458 << par->getCounter(i - 1) << '.'
1459 << par->getCounter(i);
1463 // Can this ever be reached? And in the
1464 // case it is, how can this be correct?
1466 s << par->getCounter(i) << '.';
1469 } else { // appendix
1470 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1471 case LABEL_COUNTER_CHAPTER:
1472 if (par->isRightToLeftPar(buf->params))
1473 s << hebrewCounter(par->getCounter(i));
1475 s << alphaCounter(par->getCounter(i));
1477 case LABEL_COUNTER_SECTION:
1478 if (par->isRightToLeftPar(buf->params))
1479 s << hebrewCounter(par->getCounter(i - 1));
1481 s << alphaCounter(par->getCounter(i - 1));
1484 << par->getCounter(i);
1487 case LABEL_COUNTER_SUBSECTION:
1488 if (par->isRightToLeftPar(buf->params))
1489 s << hebrewCounter(par->getCounter(i - 2));
1491 s << alphaCounter(par->getCounter(i - 2));
1494 << par->getCounter(i-1) << '.'
1495 << par->getCounter(i);
1498 case LABEL_COUNTER_SUBSUBSECTION:
1499 if (par->isRightToLeftPar(buf->params))
1500 s << hebrewCounter(par->getCounter(i-3));
1502 s << alphaCounter(par->getCounter(i-3));
1505 << par->getCounter(i-2) << '.'
1506 << par->getCounter(i-1) << '.'
1507 << par->getCounter(i);
1510 case LABEL_COUNTER_PARAGRAPH:
1511 if (par->isRightToLeftPar(buf->params))
1512 s << hebrewCounter(par->getCounter(i-4));
1514 s << alphaCounter(par->getCounter(i-4));
1517 << par->getCounter(i-3) << '.'
1518 << par->getCounter(i-2) << '.'
1519 << par->getCounter(i-1) << '.'
1520 << par->getCounter(i);
1523 case LABEL_COUNTER_SUBPARAGRAPH:
1524 if (par->isRightToLeftPar(buf->params))
1525 s << hebrewCounter(par->getCounter(i-5));
1527 s << alphaCounter(par->getCounter(i-5));
1530 << par->getCounter(i-4) << '.'
1531 << par->getCounter(i-3) << '.'
1532 << par->getCounter(i-2) << '.'
1533 << par->getCounter(i-1) << '.'
1534 << par->getCounter(i);
1538 // Can this ever be reached? And in the
1539 // case it is, how can this be correct?
1541 s << par->getCounter(i) << '.';
1547 par->params().labelString(par->params().labelString() +s.str().c_str());
1548 // We really want to remove the c_str as soon as
1551 for (i++; i < 10; ++i) {
1552 // reset the following counters
1553 par->setCounter(i, 0);
1555 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1556 for (i++; i < 10; ++i) {
1557 // reset the following counters
1558 par->setCounter(i, 0);
1560 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1561 par->incCounter(i + par->enumdepth);
1562 int number = par->getCounter(i + par->enumdepth);
1566 switch (par->enumdepth) {
1568 if (par->isRightToLeftPar(buf->params))
1570 << hebrewCounter(number)
1574 << loweralphaCounter(number)
1578 if (par->isRightToLeftPar(buf->params))
1579 s << '.' << romanCounter(number);
1581 s << romanCounter(number) << '.';
1584 if (par->isRightToLeftPar(buf->params))
1586 << alphaCounter(number);
1588 s << alphaCounter(number)
1592 if (par->isRightToLeftPar(buf->params))
1599 par->params().labelString(s.str().c_str());
1601 for (i += par->enumdepth + 1; i < 10; ++i) {
1602 // reset the following counters
1603 par->setCounter(i, 0);
1607 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1608 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1610 int number = par->getCounter(i);
1612 InsetCommandParams p( "bibitem" );
1613 par->bibkey = new InsetBibKey(p);
1615 par->bibkey->setCounter(number);
1616 par->params().labelString(layout.labelstring());
1618 // In biblio should't be following counters but...
1620 string s = layout.labelstring();
1622 // the caption hack:
1623 if (layout.labeltype == LABEL_SENSITIVE) {
1624 bool isOK (par->inInset() && par->inInset()->owner() &&
1625 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1628 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1630 = floatList.getType(tmp->type());
1631 // We should get the correct number here too.
1632 s = fl.name() + " #:";
1634 /* par->SetLayout(0);
1635 s = layout->labelstring; */
1636 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1637 ? " :úåòîùî øñç" : "Senseless: ";
1640 par->params().labelString(s);
1642 /* reset the enumeration counter. They are always resetted
1643 * when there is any other layout between */
1644 for (int i = 6 + par->enumdepth; i < 10; ++i)
1645 par->setCounter(i, 0);
1650 // Updates all counters BEHIND the row. Changed paragraphs
1651 // with a dynamic left margin will be rebroken.
1652 void LyXText::updateCounters(BufferView * bview, Row * row) const
1660 par = row->par()->next();
1664 while (row->par() != par)
1667 setCounter(bview->buffer(), par);
1669 // now check for the headline layouts. remember that they
1670 // have a dynamic left margin
1671 if ((textclasslist.Style(bview->buffer()->params.textclass,
1672 par->layout).margintype == MARGIN_DYNAMIC
1673 || textclasslist.Style(bview->buffer()->params.textclass,
1674 par->layout).labeltype == LABEL_SENSITIVE)) {
1676 // Rebreak the paragraph
1677 removeParagraph(row);
1678 appendParagraph(bview, row);
1685 void LyXText::insertInset(BufferView * bview, Inset * inset)
1687 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1689 setUndo(bview, Undo::INSERT,
1690 cursor.par(), cursor.par()->next());
1691 cursor.par()->insertInset(cursor.pos(), inset);
1692 // Just to rebreak and refresh correctly.
1693 // The character will not be inserted a second time
1694 insertChar(bview, Paragraph::META_INSET);
1696 // If we enter a highly editable inset the cursor should be to before
1697 // the inset. This couldn't happen before as Undo was not handled inside
1698 // inset now after the Undo LyX tries to call inset->Edit(...) again
1699 // and cannot do this as the cursor is behind the inset and GetInset
1700 // does not return the inset!
1701 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1702 cursorLeft(bview, true);
1708 void LyXText::copyEnvironmentType()
1710 copylayouttype = cursor.par()->getLayout();
1714 void LyXText::pasteEnvironmentType(BufferView * bview)
1716 setLayout(bview, copylayouttype);
1720 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1722 // Stuff what we got on the clipboard. Even if there is no selection.
1724 // There is a problem with having the stuffing here in that the
1725 // larger the selection the slower LyX will get. This can be
1726 // solved by running the line below only when the selection has
1727 // finished. The solution used currently just works, to make it
1728 // faster we need to be more clever and probably also have more
1729 // calls to stuffClipboard. (Lgb)
1730 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1732 // This doesn't make sense, if there is no selection
1733 if (!selection.set())
1736 // OK, we have a selection. This is always between selection.start
1737 // and selection.end
1739 // make sure that the depth behind the selection are restored, too
1740 Paragraph * endpar = selection.end.par()->next();
1741 Paragraph * undoendpar = endpar;
1743 if (endpar && endpar->getDepth()) {
1744 while (endpar && endpar->getDepth()) {
1745 endpar = endpar->next();
1746 undoendpar = endpar;
1748 } else if (endpar) {
1749 endpar = endpar->next(); // because of parindents etc.
1752 setUndo(bview, Undo::DELETE,
1753 selection.start.par(), undoendpar);
1755 // there are two cases: cut only within one paragraph or
1756 // more than one paragraph
1757 if (selection.start.par() == selection.end.par()) {
1758 // only within one paragraph
1759 endpar = selection.end.par();
1760 int pos = selection.end.pos();
1761 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1762 selection.start.pos(), pos,
1763 bview->buffer()->params.textclass, doclear,
1765 selection.end.pos(pos);
1767 endpar = selection.end.par();
1768 int pos = selection.end.pos();
1769 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1770 selection.start.pos(), pos,
1771 bview->buffer()->params.textclass, doclear,
1774 selection.end.par(endpar);
1775 selection.end.pos(pos);
1776 cursor.pos(selection.end.pos());
1778 endpar = endpar->next();
1780 // sometimes necessary
1782 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1784 redoParagraphs(bview, selection.start, endpar);
1786 // cutSelection can invalidate the cursor so we need to set
1788 cursor = selection.start;
1790 // need a valid cursor. (Lgb)
1793 setCursor(bview, cursor.par(), cursor.pos());
1794 selection.cursor = cursor;
1795 updateCounters(bview, cursor.row());
1799 void LyXText::copySelection(BufferView * bview)
1801 // Stuff what we got on the clipboard. Even if there is no selection.
1803 // There is a problem with having the stuffing here in that the
1804 // larger the selection the slower LyX will get. This can be
1805 // solved by running the line below only when the selection has
1806 // finished. The solution used currently just works, to make it
1807 // faster we need to be more clever and probably also have more
1808 // calls to stuffClipboard. (Lgb)
1809 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1811 // this doesnt make sense, if there is no selection
1812 if (!selection.set())
1815 // ok we have a selection. This is always between selection.start
1816 // and sel_end cursor
1818 // copy behind a space if there is one
1819 while (selection.start.par()->size() > selection.start.pos()
1820 && selection.start.par()->isLineSeparator(selection.start.pos())
1821 && (selection.start.par() != selection.end.par()
1822 || selection.start.pos() < selection.end.pos()))
1823 selection.start.pos(selection.start.pos() + 1);
1825 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1826 selection.start.pos(), selection.end.pos(),
1827 bview->buffer()->params.textclass);
1831 void LyXText::pasteSelection(BufferView * bview)
1833 // this does not make sense, if there is nothing to paste
1834 if (!CutAndPaste::checkPastePossible(cursor.par()))
1837 setUndo(bview, Undo::INSERT,
1838 cursor.par(), cursor.par()->next());
1841 Paragraph * actpar = cursor.par();
1842 int pos = cursor.pos();
1844 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1845 bview->buffer()->params.textclass);
1847 redoParagraphs(bview, cursor, endpar);
1849 setCursor(bview, cursor.par(), cursor.pos());
1852 setCursor(bview, actpar, pos);
1853 updateCounters(bview, cursor.row());
1857 // returns a pointer to the very first Paragraph
1858 Paragraph * LyXText::firstParagraph() const
1860 return ownerParagraph();
1864 // sets the selection over the number of characters of string, no check!!
1865 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1870 selection.cursor = cursor;
1871 for (string::size_type i = 0; i < str.length(); ++i)
1873 setSelection(bview);
1877 // simple replacing. The font of the first selected character is used
1878 void LyXText::replaceSelectionWithString(BufferView * bview,
1881 setCursorParUndo(bview);
1884 if (!selection.set()) { // create a dummy selection
1885 selection.end = cursor;
1886 selection.start = cursor;
1889 // Get font setting before we cut
1890 Paragraph::size_type pos = selection.end.pos();
1891 LyXFont const font = selection.start.par()
1892 ->getFontSettings(bview->buffer()->params,
1893 selection.start.pos());
1895 // Insert the new string
1896 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1897 selection.end.par()->insertChar(pos, (*cit), font);
1901 // Cut the selection
1902 cutSelection(bview, true, false);
1908 // needed to insert the selection
1909 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1911 Paragraph * par = cursor.par();
1912 Paragraph::size_type pos = cursor.pos();
1913 Paragraph * endpar = cursor.par()->next();
1915 setCursorParUndo(bview);
1917 // only to be sure, should not be neccessary
1920 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1922 redoParagraphs(bview, cursor, endpar);
1923 setCursor(bview, cursor.par(), cursor.pos());
1924 selection.cursor = cursor;
1925 setCursor(bview, par, pos);
1926 setSelection(bview);
1930 // turns double-CR to single CR, others where converted into one
1931 // blank. Then InsertStringAsLines is called
1932 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1934 string linestr(str);
1935 bool newline_inserted = false;
1936 for (string::size_type i = 0; i < linestr.length(); ++i) {
1937 if (linestr[i] == '\n') {
1938 if (newline_inserted) {
1939 // we know that \r will be ignored by
1940 // InsertStringA. Of course, it is a dirty
1941 // trick, but it works...
1942 linestr[i - 1] = '\r';
1946 newline_inserted = true;
1948 } else if (IsPrintable(linestr[i])) {
1949 newline_inserted = false;
1952 insertStringAsLines(bview, linestr);
1956 bool LyXText::gotoNextInset(BufferView * bview,
1957 std::vector<Inset::Code> const & codes,
1958 string const & contents) const
1960 LyXCursor res = cursor;
1963 if (res.pos() < res.par()->size() - 1) {
1964 res.pos(res.pos() + 1);
1966 res.par(res.par()->next());
1970 } while (res.par() &&
1971 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1972 && (inset = res.par()->getInset(res.pos())) != 0
1973 && find(codes.begin(), codes.end(), inset->lyxCode())
1975 && (contents.empty() ||
1976 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1980 setCursor(bview, res.par(), res.pos());
1987 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1988 Paragraph::size_type pos)
1990 LyXCursor tmpcursor;
1993 Paragraph::size_type z;
1994 Row * row = getRow(par, pos, y);
1996 // is there a break one row above
1997 if (row->previous() && row->previous()->par() == row->par()) {
1998 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1999 if (z >= row->pos()) {
2000 // set the dimensions of the row above
2001 y -= row->previous()->height();
2003 refresh_row = row->previous();
2004 status(bview, LyXText::NEED_MORE_REFRESH);
2006 breakAgain(bview, row->previous());
2008 // set the cursor again. Otherwise
2009 // dangling pointers are possible
2010 setCursor(bview, cursor.par(), cursor.pos(),
2011 false, cursor.boundary());
2012 selection.cursor = cursor;
2017 int const tmpheight = row->height();
2018 Paragraph::size_type const tmplast = rowLast(row);
2022 breakAgain(bview, row);
2023 if (row->height() == tmpheight && rowLast(row) == tmplast)
2024 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2026 status(bview, LyXText::NEED_MORE_REFRESH);
2028 // check the special right address boxes
2029 if (textclasslist.Style(bview->buffer()->params.textclass,
2030 par->getLayout()).margintype
2031 == 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 int const column = getColumnNearX(bview, row, x, bound);
2278 cur.par(row->par());
2279 cur.pos(row->pos() + column);
2281 cur.y(y + row->baseline());
2283 cur.boundary(bound);
2287 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2289 if (cursor.pos() > 0) {
2290 bool boundary = cursor.boundary();
2291 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2292 if (!internal && !boundary &&
2293 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2294 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2295 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2296 Paragraph * par = cursor.par()->previous();
2297 setCursor(bview, par, par->size());
2302 void LyXText::cursorRight(BufferView * bview, bool internal) const
2304 if (!internal && cursor.boundary() &&
2305 !cursor.par()->isNewline(cursor.pos()))
2306 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2307 else if (cursor.pos() < cursor.par()->size()) {
2308 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2310 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2311 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2312 } else if (cursor.par()->next())
2313 setCursor(bview, cursor.par()->next(), 0);
2317 void LyXText::cursorUp(BufferView * bview) const
2319 setCursorFromCoordinates(bview, cursor.x_fix(),
2320 cursor.y() - cursor.row()->baseline() - 1);
2324 void LyXText::cursorDown(BufferView * bview) const
2326 setCursorFromCoordinates(bview, cursor.x_fix(),
2327 cursor.y() - cursor.row()->baseline()
2328 + cursor.row()->height() + 1);
2332 void LyXText::cursorUpParagraph(BufferView * bview) const
2334 if (cursor.pos() > 0) {
2335 setCursor(bview, cursor.par(), 0);
2337 else if (cursor.par()->previous()) {
2338 setCursor(bview, cursor.par()->previous(), 0);
2343 void LyXText::cursorDownParagraph(BufferView * bview) const
2345 if (cursor.par()->next()) {
2346 setCursor(bview, cursor.par()->next(), 0);
2348 setCursor(bview, cursor.par(), cursor.par()->size());
2352 // fix the cursor `cur' after a characters has been deleted at `where'
2353 // position. Called by deleteEmptyParagraphMechanism
2354 void LyXText::fixCursorAfterDelete(BufferView * bview,
2356 LyXCursor const & where) const
2358 // if cursor is not in the paragraph where the delete occured,
2360 if (cur.par() != where.par())
2363 // if cursor position is after the place where the delete occured,
2365 if (cur.pos() > where.pos())
2366 cur.pos(cur.pos()-1);
2368 // recompute row et al. for this cursor
2369 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2373 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2374 LyXCursor const & old_cursor) const
2376 // Would be wrong to delete anything if we have a selection.
2377 if (selection.set()) return;
2379 // We allow all kinds of "mumbo-jumbo" when freespacing.
2380 if (textclasslist.Style(bview->buffer()->params.textclass,
2381 old_cursor.par()->getLayout()).free_spacing)
2384 bool deleted = false;
2386 /* Ok I'll put some comments here about what is missing.
2387 I have fixed BackSpace (and thus Delete) to not delete
2388 double-spaces automagically. I have also changed Cut,
2389 Copy and Paste to hopefully do some sensible things.
2390 There are still some small problems that can lead to
2391 double spaces stored in the document file or space at
2392 the beginning of paragraphs. This happens if you have
2393 the cursor betwenn to spaces and then save. Or if you
2394 cut and paste and the selection have a space at the
2395 beginning and then save right after the paste. I am
2396 sure none of these are very hard to fix, but I will
2397 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2398 that I can get some feedback. (Lgb)
2401 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2402 // delete the LineSeparator.
2405 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2406 // delete the LineSeparator.
2409 // If the pos around the old_cursor were spaces, delete one of them.
2410 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2411 // Only if the cursor has really moved
2413 if (old_cursor.pos() > 0
2414 && old_cursor.pos() < old_cursor.par()->size()
2415 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2416 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2417 old_cursor.par()->erase(old_cursor.pos() - 1);
2418 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2420 #ifdef WITH_WARNINGS
2421 #warning This will not work anymore when we have multiple views of the same buffer
2422 // In this case, we will have to correct also the cursors held by
2423 // other bufferviews. It will probably be easier to do that in a more
2424 // automated way in LyXCursor code. (JMarc 26/09/2001)
2426 // correct all cursors held by the LyXText
2427 fixCursorAfterDelete(bview, cursor, old_cursor);
2428 fixCursorAfterDelete(bview, selection.cursor,
2430 fixCursorAfterDelete(bview, selection.start,
2432 fixCursorAfterDelete(bview, selection.end, old_cursor);
2433 fixCursorAfterDelete(bview, last_sel_cursor,
2435 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2436 fixCursorAfterDelete(bview, toggle_end_cursor,
2442 // Do not delete empty paragraphs with keepempty set.
2443 if ((textclasslist.Style(bview->buffer()->params.textclass,
2444 old_cursor.par()->getLayout())).keepempty)
2447 LyXCursor tmpcursor;
2449 if (old_cursor.par() != cursor.par()) {
2450 if ((old_cursor.par()->size() == 0
2451 || (old_cursor.par()->size() == 1
2452 && old_cursor.par()->isLineSeparator(0)))) {
2453 // ok, we will delete anything
2455 // make sure that you do not delete any environments
2456 status(bview, LyXText::NEED_MORE_REFRESH);
2459 if (old_cursor.row()->previous()) {
2460 refresh_row = old_cursor.row()->previous();
2461 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2463 cursor = old_cursor; // that undo can restore the right cursor position
2464 Paragraph * endpar = old_cursor.par()->next();
2465 if (endpar && endpar->getDepth()) {
2466 while (endpar && endpar->getDepth()) {
2467 endpar = endpar->next();
2470 setUndo(bview, Undo::DELETE,
2476 removeRow(old_cursor.row());
2477 if (ownerParagraph() == old_cursor.par()) {
2478 ownerParagraph(ownerParagraph()->next());
2481 delete old_cursor.par();
2483 /* Breakagain the next par. Needed
2484 * because of the parindent that
2485 * can occur or dissappear. The
2486 * next row can change its height,
2487 * if 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,
2511 removeRow(old_cursor.row());
2513 if (ownerParagraph() == old_cursor.par()) {
2514 ownerParagraph(ownerParagraph()->next());
2517 delete old_cursor.par();
2519 /* Breakagain the next par. Needed
2520 because of the parindent that can
2521 occur or dissappear.
2522 The next row can change its height,
2523 if there is another layout before
2526 breakAgain(bview, refresh_row);
2527 updateCounters(bview, refresh_row->previous());
2533 setCursorIntern(bview, cursor.par(), cursor.pos());
2535 if (selection.cursor.par() == old_cursor.par()
2536 && selection.cursor.pos() == selection.cursor.pos()) {
2537 // correct selection
2538 selection.cursor = cursor;
2542 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2543 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2545 setCursorIntern(bview, cursor.par(), cursor.pos());
2546 selection.cursor = cursor;
2553 void LyXText::toggleAppendix(BufferView * bview)
2555 Paragraph * par = cursor.par();
2556 bool start = !par->params().startOfAppendix();
2558 // ensure that we have only one start_of_appendix in this document
2559 Paragraph * tmp = firstParagraph();
2560 for (; tmp; tmp = tmp->next()) {
2561 tmp->params().startOfAppendix(false);
2564 par->params().startOfAppendix(start);
2566 // we can set the refreshing parameters now
2567 status(bview, LyXText::NEED_MORE_REFRESH);
2569 refresh_row = 0; // not needed for full update
2570 updateCounters(bview, 0);
2571 setCursor(bview, cursor.par(), cursor.pos());
2575 Paragraph * LyXText::ownerParagraph() const
2578 return inset_owner->paragraph();
2580 return bv_owner->buffer()->paragraph;
2584 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2587 inset_owner->paragraph(p);
2589 bv_owner->buffer()->paragraph = p;
2594 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2596 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2597 if (op && op->inInset()) {
2598 static_cast<InsetText *>(op->inInset())->paragraph(p);
2601 inset_owner->paragraph(p);
2603 bv_owner->buffer()->paragraph = p;
2610 LyXText::text_status LyXText::status() const
2616 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2618 // well as much as I know && binds more then || so the above and the
2619 // below are identical (this for your known use of parentesis!)
2620 // Now some explanation:
2621 // We should only go up with refreshing code so this means that if
2622 // we have a MORE refresh we should never set it to LITTLE if we still
2623 // didn't handle it (and then it will be UNCHANGED. Now as long as
2624 // we stay inside one LyXText this may work but we need to tell the
2625 // outermost LyXText that it should REALLY draw us if there is some
2626 // change in a Inset::LyXText. So you see that when we are inside a
2627 // inset's LyXText we give the LITTLE to the outermost LyXText to
2628 // tell'em that it should redraw the actual row (where the inset
2629 // resides! Capito?!
2631 if ((status_ != NEED_MORE_REFRESH)
2632 || (status_ == NEED_MORE_REFRESH
2633 && st != NEED_VERY_LITTLE_REFRESH))
2636 if (inset_owner && st != UNCHANGED) {
2637 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2638 if (!bview->text->refresh_row) {
2639 bview->text->refresh_row = bview->text->cursor.row();
2640 bview->text->refresh_y = bview->text->cursor.y() -
2641 bview->text->cursor.row()->baseline();