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 (isMetaInset(par, pos)) {
250 Inset * inset = par->getInset(pos);
251 if (isEditableInset(inset)) {
252 UpdatableInset * uinset =
253 static_cast<UpdatableInset *>(inset);
254 uinset->setFont(bv, fnt, toggleall, true);
258 LyXLayout const & layout =
259 textclasslist.Style(buf->params.textclass,
262 // Get concrete layout font to reduce against
265 if (pos < beginningOfMainBody(buf, par))
266 layoutfont = layout.labelfont;
268 layoutfont = layout.font;
270 // Realize against environment font information
271 if (par->getDepth()){
272 Paragraph * tp = par;
273 while (!layoutfont.resolved() && tp && tp->getDepth()) {
274 tp = tp->outerHook();
276 #ifndef INHERIT_LANGUAGE
277 layoutfont.realize(textclasslist.
278 Style(buf->params.textclass,
279 tp->getLayout()).font);
281 layoutfont.realize(textclasslist.
282 Style(buf->params.textclass,
283 tp->getLayout()).font,
284 buf->params.language);
289 #ifndef INHERIT_LANGUAGE
290 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
292 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
293 buf->params.language);
296 // Now, reduce font against full layout font
297 font.reduce(layoutfont);
299 par->setFont(pos, font);
303 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
304 Paragraph::size_type pos, LyXFont const & fnt)
308 LyXLayout const & layout =
309 textclasslist.Style(buf->params.textclass,
312 // Get concrete layout font to reduce against
315 if (pos < beginningOfMainBody(buf, par))
316 layoutfont = layout.labelfont;
318 layoutfont = layout.font;
320 // Realize against environment font information
321 if (par->getDepth()){
322 Paragraph * tp = par;
323 while (!layoutfont.resolved() && tp && tp->getDepth()) {
324 tp = tp->outerHook();
326 #ifndef INHERIT_LANGUAGE
327 layoutfont.realize(textclasslist.
328 Style(buf->params.textclass,
329 tp->getLayout()).font);
331 layoutfont.realize(textclasslist.
332 Style(buf->params.textclass,
333 tp->getLayout()).font,
334 buf->params.language);
339 #ifndef INHERIT_LANGUAGE
340 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
342 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
343 buf->params.language);
346 // Now, reduce font against full layout font
347 font.reduce(layoutfont);
349 par->setFont(pos, font);
353 // inserts a new row behind the specified row, increments
354 // the touched counters
355 void LyXText::insertRow(Row * row, Paragraph * par,
356 Paragraph::size_type pos) const
358 Row * tmprow = new Row;
361 tmprow->next(firstrow);
364 tmprow->previous(row);
365 tmprow->next(row->next());
370 tmprow->next()->previous(tmprow);
372 if (tmprow->previous())
373 tmprow->previous()->next(tmprow);
385 // removes the row and reset the touched counters
386 void LyXText::removeRow(Row * row) const
388 /* this must not happen before the currentrow for clear reasons.
389 so the trick is just to set the current row onto the previous
392 getRow(row->par(), row->pos(), unused_y);
395 row->next()->previous(row->previous());
396 if (!row->previous()) {
397 firstrow = row->next();
399 row->previous()->next(row->next());
402 lastrow = row->previous();
404 height -= row->height(); // the text becomes smaller
407 --number_of_rows; // one row less
411 // remove all following rows of the paragraph of the specified row.
412 void LyXText::removeParagraph(Row * row) const
414 Paragraph * tmppar = row->par();
418 while (row && row->par() == tmppar) {
419 tmprow = row->next();
426 // insert the specified paragraph behind the specified row
427 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
430 insertRow(row, par, 0); /* insert a new row, starting
433 setCounter(bview->buffer(), par); // set the counters
435 // and now append the whole paragraph behind the new row
438 appendParagraph(bview, firstrow);
440 row->next()->height(0);
441 appendParagraph(bview, row->next());
446 Inset * LyXText::getInset() const
449 if (cursor.pos() == 0 && cursor.par()->bibkey) {
450 inset = cursor.par()->bibkey;
451 } else if (cursor.pos() < cursor.par()->size()
452 && isMetaInset(cursor.par(), cursor.pos())) {
453 inset = cursor.par()->getInset(cursor.pos());
459 void LyXText::toggleInset(BufferView * bview)
461 Inset * inset = getInset();
462 if (!isEditableInset(inset))
464 //bview->owner()->message(inset->editMessage());
466 // do we want to keep this?? (JMarc)
467 if (!isHighlyEditableInset(inset))
468 setCursorParUndo(bview);
470 if (inset->isOpen()) {
476 inset->open(bview, !inset->isOpen());
481 /* used in setlayout */
482 // Asger is not sure we want to do this...
483 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
486 LyXLayout const & layout =
487 textclasslist.Style(buf->params.textclass, par->getLayout());
490 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
491 if (pos < beginningOfMainBody(buf, par))
492 layoutfont = layout.labelfont;
494 layoutfont = layout.font;
496 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
497 tmpfont.reduce(layoutfont);
498 par->setFont(pos, tmpfont);
503 Paragraph * LyXText::setLayout(BufferView * bview,
504 LyXCursor & cur, LyXCursor & sstart_cur,
505 LyXCursor & send_cur,
506 LyXTextClass::size_type layout)
508 Paragraph * endpar = send_cur.par()->next();
509 Paragraph * undoendpar = endpar;
511 if (endpar && endpar->getDepth()) {
512 while (endpar && endpar->getDepth()) {
513 endpar = endpar->next();
517 endpar = endpar->next(); // because of parindents etc.
520 setUndo(bview, Undo::EDIT,
521 sstart_cur.par(), undoendpar);
523 // ok we have a selection. This is always between sstart_cur
524 // and sel_end cursor
527 LyXLayout const & lyxlayout =
528 textclasslist.Style(bview->buffer()->params.textclass, layout);
530 while (cur.par() != send_cur.par()) {
531 cur.par()->setLayout(layout);
532 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
533 Paragraph * fppar = cur.par();
534 fppar->params().spaceTop(lyxlayout.fill_top ?
535 VSpace(VSpace::VFILL)
536 : VSpace(VSpace::NONE));
537 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
538 VSpace(VSpace::VFILL)
539 : VSpace(VSpace::NONE));
540 if (lyxlayout.margintype == MARGIN_MANUAL)
541 cur.par()->setLabelWidthString(lyxlayout.labelstring());
542 if (lyxlayout.labeltype != LABEL_BIBLIO
544 delete fppar->bibkey;
547 cur.par(cur.par()->next());
549 cur.par()->setLayout(layout);
550 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
551 Paragraph * fppar = cur.par();
552 fppar->params().spaceTop(lyxlayout.fill_top ?
553 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
554 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
555 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
556 if (lyxlayout.margintype == MARGIN_MANUAL)
557 cur.par()->setLabelWidthString(lyxlayout.labelstring());
558 if (lyxlayout.labeltype != LABEL_BIBLIO
560 delete fppar->bibkey;
567 // set layout over selection and make a total rebreak of those paragraphs
568 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
570 LyXCursor tmpcursor = cursor; /* store the current cursor */
572 // if there is no selection just set the layout
573 // of the current paragraph */
574 if (!selection.set()) {
575 selection.start = cursor; // dummy selection
576 selection.end = cursor;
578 Paragraph * endpar = setLayout(bview, cursor, selection.start,
579 selection.end, layout);
580 redoParagraphs(bview, selection.start, endpar);
582 // we have to reset the selection, because the
583 // geometry could have changed
584 setCursor(bview, selection.start.par(),
585 selection.start.pos(), false);
586 selection.cursor = cursor;
587 setCursor(bview, selection.end.par(), selection.end.pos(), false);
588 updateCounters(bview, cursor.row());
591 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
595 // increment depth over selection and
596 // make a total rebreak of those paragraphs
597 void LyXText::incDepth(BufferView * bview)
599 // If there is no selection, just use the current paragraph
600 if (!selection.set()) {
601 selection.start = cursor; // dummy selection
602 selection.end = cursor;
605 // We end at the next paragraph with depth 0
606 Paragraph * endpar = selection.end.par()->next();
608 Paragraph * undoendpar = endpar;
610 if (endpar && endpar->getDepth()) {
611 while (endpar && endpar->getDepth()) {
612 endpar = endpar->next();
616 endpar = endpar->next(); // because of parindents etc.
619 setUndo(bview, Undo::EDIT,
620 selection.start.par(), undoendpar);
622 LyXCursor tmpcursor = cursor; // store the current cursor
624 // ok we have a selection. This is always between sel_start_cursor
625 // and sel_end cursor
626 cursor = selection.start;
628 bool anything_changed = false;
631 // NOTE: you can't change the depth of a bibliography entry
633 textclasslist.Style(bview->buffer()->params.textclass,
634 cursor.par()->getLayout()
635 ).labeltype != LABEL_BIBLIO) {
636 Paragraph * prev = cursor.par()->previous();
639 && (prev->getDepth() - cursor.par()->getDepth() > 0
640 || (prev->getDepth() == cursor.par()->getDepth()
641 && textclasslist.Style(bview->buffer()->params.textclass,
642 prev->getLayout()).isEnvironment()))) {
643 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
644 anything_changed = true;
647 if (cursor.par() == selection.end.par())
649 cursor.par(cursor.par()->next());
652 // if nothing changed set all depth to 0
653 if (!anything_changed) {
654 cursor = selection.start;
655 while (cursor.par() != selection.end.par()) {
656 cursor.par()->params().depth(0);
657 cursor.par(cursor.par()->next());
659 cursor.par()->params().depth(0);
662 redoParagraphs(bview, selection.start, endpar);
664 // we have to reset the selection, because the
665 // geometry could have changed
666 setCursor(bview, selection.start.par(), selection.start.pos());
667 selection.cursor = cursor;
668 setCursor(bview, selection.end.par(), selection.end.pos());
669 updateCounters(bview, cursor.row());
672 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
676 // decrement depth over selection and
677 // make a total rebreak of those paragraphs
678 void LyXText::decDepth(BufferView * bview)
680 // if there is no selection just set the layout
681 // of the current paragraph
682 if (!selection.set()) {
683 selection.start = cursor; // dummy selection
684 selection.end = cursor;
686 Paragraph * endpar = selection.end.par()->next();
687 Paragraph * undoendpar = endpar;
689 if (endpar && endpar->getDepth()) {
690 while (endpar && endpar->getDepth()) {
691 endpar = endpar->next();
695 endpar = endpar->next(); // because of parindents etc.
698 setUndo(bview, Undo::EDIT,
699 selection.start.par(), undoendpar);
701 LyXCursor tmpcursor = cursor; // store the current cursor
703 // ok we have a selection. This is always between sel_start_cursor
704 // and sel_end cursor
705 cursor = selection.start;
708 if (cursor.par()->params().depth()) {
709 cursor.par()->params()
710 .depth(cursor.par()->params().depth() - 1);
712 if (cursor.par() == selection.end.par()) {
715 cursor.par(cursor.par()->next());
718 redoParagraphs(bview, selection.start, endpar);
720 // we have to reset the selection, because the
721 // geometry could have changed
722 setCursor(bview, selection.start.par(),
723 selection.start.pos());
724 selection.cursor = cursor;
725 setCursor(bview, selection.end.par(), selection.end.pos());
726 updateCounters(bview, cursor.row());
729 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
733 // set font over selection and make a total rebreak of those paragraphs
734 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
736 // if there is no selection just set the current_font
737 if (!selection.set()) {
738 // Determine basis font
740 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
742 layoutfont = getLabelFont(bview->buffer(),
745 layoutfont = getLayoutFont(bview->buffer(),
748 // Update current font
749 real_current_font.update(font,
750 bview->buffer()->params.language,
753 // Reduce to implicit settings
754 current_font = real_current_font;
755 current_font.reduce(layoutfont);
756 // And resolve it completely
757 #ifndef INHERIT_LANGUAGE
758 real_current_font.realize(layoutfont);
760 real_current_font.realize(layoutfont,
761 bview->buffer()->params.language);
766 LyXCursor tmpcursor = cursor; // store the current cursor
768 // ok we have a selection. This is always between sel_start_cursor
769 // and sel_end cursor
771 setUndo(bview, Undo::EDIT,
772 selection.start.par(), selection.end.par()->next());
774 cursor = selection.start;
775 while (cursor.par() != selection.end.par() ||
776 (cursor.pos() < selection.end.pos()))
778 if (cursor.pos() < cursor.par()->size()) {
779 // an open footnote should behave
781 setCharFont(bview, cursor.par(), cursor.pos(),
783 cursor.pos(cursor.pos() + 1);
786 cursor.par(cursor.par()->next());
791 redoParagraphs(bview, selection.start, selection.end.par()->next());
793 // we have to reset the selection, because the
794 // geometry could have changed
795 setCursor(bview, selection.start.par(), selection.start.pos());
796 selection.cursor = cursor;
797 setCursor(bview, selection.end.par(), selection.end.pos());
800 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
801 tmpcursor.boundary());
805 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
807 Row * tmprow = cur.row();
808 int y = cur.y() - tmprow->baseline();
810 setHeightOfRow(bview, tmprow);
812 while (tmprow->previous()
813 && tmprow->previous()->par() == tmprow->par()) {
814 tmprow = tmprow->previous();
815 y -= tmprow->height();
816 setHeightOfRow(bview, tmprow);
819 // we can set the refreshing parameters now
820 status(bview, LyXText::NEED_MORE_REFRESH);
822 refresh_row = tmprow;
823 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
827 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
829 Row * tmprow = cur.row();
831 int y = cur.y() - tmprow->baseline();
832 setHeightOfRow(bview, tmprow);
834 while (tmprow->previous()
835 && tmprow->previous()->par() == tmprow->par()) {
836 tmprow = tmprow->previous();
837 y -= tmprow->height();
840 // we can set the refreshing parameters now
841 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
843 refresh_row = tmprow;
845 status(bview, LyXText::NEED_MORE_REFRESH);
846 setCursor(bview, cur.par(), cur.pos());
850 // deletes and inserts again all paragaphs between the cursor
851 // and the specified par
852 // This function is needed after SetLayout and SetFont etc.
853 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
854 Paragraph const * endpar) const
857 Paragraph * tmppar = 0;
858 Paragraph * first_phys_par = 0;
860 Row * tmprow = cur.row();
862 int y = cur.y() - tmprow->baseline();
864 if (!tmprow->previous()) {
865 // a trick/hack for UNDO
866 // Can somebody please tell me _why_ this solves
868 first_phys_par = firstParagraph();
870 first_phys_par = tmprow->par();
871 while (tmprow->previous()
872 && tmprow->previous()->par() == first_phys_par)
874 tmprow = tmprow->previous();
875 y -= tmprow->height();
879 // we can set the refreshing parameters now
880 status(bview, LyXText::NEED_MORE_REFRESH);
882 refresh_row = tmprow->previous(); /* the real refresh row will
883 be deleted, so I store
887 tmppar = tmprow->next()->par();
890 while (tmppar != endpar) {
891 removeRow(tmprow->next());
893 tmppar = tmprow->next()->par();
898 // remove the first one
899 tmprow2 = tmprow; /* this is because tmprow->previous()
901 tmprow = tmprow->previous();
904 tmppar = first_phys_par;
908 insertParagraph(bview, tmppar, tmprow);
912 while (tmprow->next()
913 && tmprow->next()->par() == tmppar) {
914 tmprow = tmprow->next();
916 tmppar = tmppar->next();
918 } while (tmppar && tmppar != endpar);
920 // this is because of layout changes
922 refresh_y -= refresh_row->height();
923 setHeightOfRow(bview, refresh_row);
925 refresh_row = firstrow;
927 setHeightOfRow(bview, refresh_row);
930 if (tmprow && tmprow->next())
931 setHeightOfRow(bview, tmprow->next());
935 bool LyXText::fullRebreak(BufferView * bview)
941 if (need_break_row) {
942 breakAgain(bview, need_break_row);
950 // important for the screen
953 /* the cursor set functions have a special mechanism. When they
954 * realize, that you left an empty paragraph, they will delete it.
955 * They also delete the corresponding row */
957 // need the selection cursor:
958 void LyXText::setSelection(BufferView * bview)
960 bool const lsel = selection.set();
962 if (!selection.set()) {
963 last_sel_cursor = selection.cursor;
964 selection.start = selection.cursor;
965 selection.end = selection.cursor;
970 // first the toggling area
971 if (cursor.y() < last_sel_cursor.y()
972 || (cursor.y() == last_sel_cursor.y()
973 && cursor.x() < last_sel_cursor.x())) {
974 toggle_end_cursor = last_sel_cursor;
975 toggle_cursor = cursor;
977 toggle_end_cursor = cursor;
978 toggle_cursor = last_sel_cursor;
981 last_sel_cursor = cursor;
983 // and now the whole selection
985 if (selection.cursor.par() == cursor.par())
986 if (selection.cursor.pos() < cursor.pos()) {
987 selection.end = cursor;
988 selection.start = selection.cursor;
990 selection.end = selection.cursor;
991 selection.start = cursor;
993 else if (selection.cursor.y() < cursor.y() ||
994 (selection.cursor.y() == cursor.y()
995 && selection.cursor.x() < cursor.x())) {
996 selection.end = cursor;
997 selection.start = selection.cursor;
1000 selection.end = selection.cursor;
1001 selection.start = cursor;
1004 // a selection with no contents is not a selection
1005 if (selection.start.par() == selection.end.par() &&
1006 selection.start.pos() == selection.end.pos())
1007 selection.set(false);
1009 if (inset_owner && (selection.set() || lsel))
1010 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1014 string const LyXText::selectionAsString(Buffer const * buffer,
1017 if (!selection.set()) return string();
1020 // Special handling if the whole selection is within one paragraph
1021 if (selection.start.par() == selection.end.par()) {
1022 result += selection.start.par()->asString(buffer,
1023 selection.start.pos(),
1024 selection.end.pos(),
1029 // The selection spans more than one paragraph
1031 // First paragraph in selection
1032 result += selection.start.par()->asString(buffer,
1033 selection.start.pos(),
1034 selection.start.par()->size(),
1038 // The paragraphs in between (if any)
1039 LyXCursor tmpcur(selection.start);
1040 tmpcur.par(tmpcur.par()->next());
1041 while (tmpcur.par() != selection.end.par()) {
1042 result += tmpcur.par()->asString(buffer, 0,
1043 tmpcur.par()->size(),
1045 tmpcur.par(tmpcur.par()->next());
1048 // Last paragraph in selection
1049 result += selection.end.par()->asString(buffer, 0,
1050 selection.end.pos(), label);
1056 void LyXText::clearSelection() const
1058 selection.set(false);
1059 selection.mark(false);
1060 selection.end = selection.start = selection.cursor = cursor;
1064 void LyXText::cursorHome(BufferView * bview) const
1066 setCursor(bview, cursor.par(), cursor.row()->pos());
1070 void LyXText::cursorEnd(BufferView * bview) const
1072 if (!cursor.row()->next()
1073 || cursor.row()->next()->par() != cursor.row()->par()) {
1074 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1076 if (cursor.par()->size() &&
1077 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1078 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1079 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1081 setCursor(bview,cursor.par(),
1082 rowLast(cursor.row()) + 1);
1088 void LyXText::cursorTop(BufferView * bview) const
1090 while (cursor.par()->previous())
1091 cursor.par(cursor.par()->previous());
1092 setCursor(bview, cursor.par(), 0);
1096 void LyXText::cursorBottom(BufferView * bview) const
1098 while (cursor.par()->next())
1099 cursor.par(cursor.par()->next());
1100 setCursor(bview, cursor.par(), cursor.par()->size());
1104 void LyXText::toggleFree(BufferView * bview,
1105 LyXFont const & font, bool toggleall)
1107 // If the mask is completely neutral, tell user
1108 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1109 // Could only happen with user style
1110 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1114 // Try implicit word selection
1115 // If there is a change in the language the implicit word selection
1117 LyXCursor resetCursor = cursor;
1118 bool implicitSelection = (font.language() == ignore_language
1119 && font.number() == LyXFont::IGNORE)
1120 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1123 setFont(bview, font, toggleall);
1125 // Implicit selections are cleared afterwards
1126 //and cursor is set to the original position.
1127 if (implicitSelection) {
1129 cursor = resetCursor;
1130 setCursor(bview, cursor.par(), cursor.pos());
1131 selection.cursor = cursor;
1134 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1138 string LyXText::getStringToIndex(BufferView * bview)
1142 // Try implicit word selection
1143 // If there is a change in the language the implicit word selection
1145 LyXCursor resetCursor = cursor;
1146 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1148 if (!selection.set()) {
1149 bview->owner()->message(_("Nothing to index!"));
1152 if (selection.start.par() != selection.end.par()) {
1153 bview->owner()->message(_("Cannot index more than one paragraph!"));
1157 idxstring = selectionAsString(bview->buffer(), false);
1159 // Implicit selections are cleared afterwards
1160 //and cursor is set to the original position.
1161 if (implicitSelection) {
1163 cursor = resetCursor;
1164 setCursor(bview, cursor.par(), cursor.pos());
1165 selection.cursor = cursor;
1171 Paragraph::size_type LyXText::beginningOfMainBody(Buffer const * buf,
1172 Paragraph const * par) const
1174 if (textclasslist.Style(buf->params.textclass,
1175 par->getLayout()).labeltype != LABEL_MANUAL)
1178 return par->beginningOfMainBody();
1182 /* the DTP switches for paragraphs. LyX will store them in the
1183 * first physicla paragraph. When a paragraph is broken, the top settings
1184 * rest, the bottom settings are given to the new one. So I can make shure,
1185 * they do not duplicate themself and you cannnot make dirty things with
1188 void LyXText::setParagraph(BufferView * bview,
1189 bool line_top, bool line_bottom,
1190 bool pagebreak_top, bool pagebreak_bottom,
1191 VSpace const & space_top,
1192 VSpace const & space_bottom,
1193 Spacing const & spacing,
1195 string labelwidthstring,
1198 LyXCursor tmpcursor = cursor;
1199 if (!selection.set()) {
1200 selection.start = cursor;
1201 selection.end = cursor;
1204 // make sure that the depth behind the selection are restored, too
1205 Paragraph * endpar = selection.end.par()->next();
1206 Paragraph * undoendpar = endpar;
1208 if (endpar && endpar->getDepth()) {
1209 while (endpar && endpar->getDepth()) {
1210 endpar = endpar->next();
1211 undoendpar = endpar;
1215 // because of parindents etc.
1216 endpar = endpar->next();
1219 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1222 Paragraph * tmppar = selection.end.par();
1223 while (tmppar != selection.start.par()->previous()) {
1224 setCursor(bview, tmppar, 0);
1225 status(bview, LyXText::NEED_MORE_REFRESH);
1226 refresh_row = cursor.row();
1227 refresh_y = cursor.y() - cursor.row()->baseline();
1228 cursor.par()->params().lineTop(line_top);
1229 cursor.par()->params().lineBottom(line_bottom);
1230 cursor.par()->params().pagebreakTop(pagebreak_top);
1231 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1232 cursor.par()->params().spaceTop(space_top);
1233 cursor.par()->params().spaceBottom(space_bottom);
1234 cursor.par()->params().spacing(spacing);
1235 // does the layout allow the new alignment?
1236 if (align == LYX_ALIGN_LAYOUT)
1237 align = textclasslist
1238 .Style(bview->buffer()->params.textclass,
1239 cursor.par()->getLayout()).align;
1240 if (align & textclasslist
1241 .Style(bview->buffer()->params.textclass,
1242 cursor.par()->getLayout()).alignpossible) {
1243 if (align == textclasslist
1244 .Style(bview->buffer()->params.textclass,
1245 cursor.par()->getLayout()).align)
1246 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1248 cursor.par()->params().align(align);
1250 cursor.par()->setLabelWidthString(labelwidthstring);
1251 cursor.par()->params().noindent(noindent);
1252 tmppar = cursor.par()->previous();
1255 redoParagraphs(bview, selection.start, endpar);
1258 setCursor(bview, selection.start.par(), selection.start.pos());
1259 selection.cursor = cursor;
1260 setCursor(bview, selection.end.par(), selection.end.pos());
1261 setSelection(bview);
1262 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1264 bview->updateInset(inset_owner, true);
1268 char loweralphaCounter(int n)
1270 if (n < 1 || n > 26)
1280 char alphaCounter(int n)
1282 if (n < 1 || n > 26)
1290 char hebrewCounter(int n)
1292 static const char hebrew[22] = {
1293 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1294 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1295 '÷', 'ø', 'ù', 'ú'
1297 if (n < 1 || n > 22)
1305 string const romanCounter(int n)
1307 static char const * roman[20] = {
1308 "i", "ii", "iii", "iv", "v",
1309 "vi", "vii", "viii", "ix", "x",
1310 "xi", "xii", "xiii", "xiv", "xv",
1311 "xvi", "xvii", "xviii", "xix", "xx"
1313 if (n < 1 || n > 20)
1322 // set the counter of a paragraph. This includes the labels
1323 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1325 LyXLayout const & layout =
1326 textclasslist.Style(buf->params.textclass,
1329 LyXTextClass const & textclass =
1330 textclasslist.TextClass(buf->params.textclass);
1332 // copy the prev-counters to this one,
1333 // unless this is the first paragraph
1334 if (par->previous()) {
1335 for (int i = 0; i < 10; ++i) {
1336 par->setCounter(i, par->previous()->getFirstCounter(i));
1338 par->params().appendix(par->previous()->params().appendix());
1339 if (!par->params().appendix() && par->params().startOfAppendix()) {
1340 par->params().appendix(true);
1341 for (int i = 0; i < 10; ++i) {
1342 par->setCounter(i, 0);
1345 par->enumdepth = par->previous()->enumdepth;
1346 par->itemdepth = par->previous()->itemdepth;
1348 for (int i = 0; i < 10; ++i) {
1349 par->setCounter(i, 0);
1351 par->params().appendix(par->params().startOfAppendix());
1356 /* Maybe we have to increment the enumeration depth.
1357 * BUT, enumeration in a footnote is considered in isolation from its
1358 * surrounding paragraph so don't increment if this is the
1359 * first line of the footnote
1360 * AND, bibliographies can't have their depth changed ie. they
1361 * are always of depth 0
1364 && par->previous()->getDepth() < par->getDepth()
1365 && textclasslist.Style(buf->params.textclass,
1366 par->previous()->getLayout()
1367 ).labeltype == LABEL_COUNTER_ENUMI
1368 && par->enumdepth < 3
1369 && layout.labeltype != LABEL_BIBLIO) {
1373 // Maybe we have to decrement the enumeration depth, see note above
1375 && par->previous()->getDepth() > par->getDepth()
1376 && layout.labeltype != LABEL_BIBLIO) {
1377 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1378 par->setCounter(6 + par->enumdepth,
1379 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1380 /* reset the counters.
1381 * A depth change is like a breaking layout
1383 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1384 par->setCounter(i, 0);
1387 if (!par->params().labelString().empty()) {
1388 par->params().labelString(string());
1391 if (layout.margintype == MARGIN_MANUAL) {
1392 if (par->params().labelWidthString().empty()) {
1393 par->setLabelWidthString(layout.labelstring());
1396 par->setLabelWidthString(string());
1399 // is it a layout that has an automatic label?
1400 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1402 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1403 if (i >= 0 && i<= buf->params.secnumdepth) {
1404 par->incCounter(i); // increment the counter
1406 // Is there a label? Useful for Chapter layout
1407 if (!par->params().appendix()) {
1408 if (!layout.labelstring().empty())
1409 par->params().labelString(layout.labelstring());
1411 par->params().labelString(string());
1413 if (!layout.labelstring_appendix().empty())
1414 par->params().labelString(layout.labelstring_appendix());
1416 par->params().labelString(string());
1421 if (!par->params().appendix()) {
1422 switch (2 * LABEL_COUNTER_CHAPTER -
1423 textclass.maxcounter() + i) {
1424 case LABEL_COUNTER_CHAPTER:
1425 s << par->getCounter(i);
1427 case LABEL_COUNTER_SECTION:
1428 s << par->getCounter(i - 1) << '.'
1429 << par->getCounter(i);
1431 case LABEL_COUNTER_SUBSECTION:
1432 s << par->getCounter(i - 2) << '.'
1433 << par->getCounter(i - 1) << '.'
1434 << par->getCounter(i);
1436 case LABEL_COUNTER_SUBSUBSECTION:
1437 s << par->getCounter(i - 3) << '.'
1438 << par->getCounter(i - 2) << '.'
1439 << par->getCounter(i - 1) << '.'
1440 << par->getCounter(i);
1443 case LABEL_COUNTER_PARAGRAPH:
1444 s << par->getCounter(i - 4) << '.'
1445 << par->getCounter(i - 3) << '.'
1446 << par->getCounter(i - 2) << '.'
1447 << par->getCounter(i - 1) << '.'
1448 << par->getCounter(i);
1450 case LABEL_COUNTER_SUBPARAGRAPH:
1451 s << par->getCounter(i - 5) << '.'
1452 << par->getCounter(i - 4) << '.'
1453 << par->getCounter(i - 3) << '.'
1454 << par->getCounter(i - 2) << '.'
1455 << par->getCounter(i - 1) << '.'
1456 << par->getCounter(i);
1460 // Can this ever be reached? And in the
1461 // case it is, how can this be correct?
1463 s << par->getCounter(i) << '.';
1466 } else { // appendix
1467 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1468 case LABEL_COUNTER_CHAPTER:
1469 if (par->isRightToLeftPar(buf->params))
1470 s << hebrewCounter(par->getCounter(i));
1472 s << alphaCounter(par->getCounter(i));
1474 case LABEL_COUNTER_SECTION:
1475 if (par->isRightToLeftPar(buf->params))
1476 s << hebrewCounter(par->getCounter(i - 1));
1478 s << alphaCounter(par->getCounter(i - 1));
1481 << par->getCounter(i);
1484 case LABEL_COUNTER_SUBSECTION:
1485 if (par->isRightToLeftPar(buf->params))
1486 s << hebrewCounter(par->getCounter(i - 2));
1488 s << alphaCounter(par->getCounter(i - 2));
1491 << par->getCounter(i-1) << '.'
1492 << par->getCounter(i);
1495 case LABEL_COUNTER_SUBSUBSECTION:
1496 if (par->isRightToLeftPar(buf->params))
1497 s << hebrewCounter(par->getCounter(i-3));
1499 s << alphaCounter(par->getCounter(i-3));
1502 << par->getCounter(i-2) << '.'
1503 << par->getCounter(i-1) << '.'
1504 << par->getCounter(i);
1507 case LABEL_COUNTER_PARAGRAPH:
1508 if (par->isRightToLeftPar(buf->params))
1509 s << hebrewCounter(par->getCounter(i-4));
1511 s << alphaCounter(par->getCounter(i-4));
1514 << par->getCounter(i-3) << '.'
1515 << par->getCounter(i-2) << '.'
1516 << par->getCounter(i-1) << '.'
1517 << par->getCounter(i);
1520 case LABEL_COUNTER_SUBPARAGRAPH:
1521 if (par->isRightToLeftPar(buf->params))
1522 s << hebrewCounter(par->getCounter(i-5));
1524 s << alphaCounter(par->getCounter(i-5));
1527 << par->getCounter(i-4) << '.'
1528 << par->getCounter(i-3) << '.'
1529 << par->getCounter(i-2) << '.'
1530 << par->getCounter(i-1) << '.'
1531 << par->getCounter(i);
1535 // Can this ever be reached? And in the
1536 // case it is, how can this be correct?
1538 s << par->getCounter(i) << '.';
1544 par->params().labelString(par->params().labelString() +s.str().c_str());
1545 // We really want to remove the c_str as soon as
1548 for (i++; i < 10; ++i) {
1549 // reset the following counters
1550 par->setCounter(i, 0);
1552 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1553 for (i++; i < 10; ++i) {
1554 // reset the following counters
1555 par->setCounter(i, 0);
1557 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1558 par->incCounter(i + par->enumdepth);
1559 int number = par->getCounter(i + par->enumdepth);
1563 switch (par->enumdepth) {
1565 if (par->isRightToLeftPar(buf->params))
1567 << hebrewCounter(number)
1571 << loweralphaCounter(number)
1575 if (par->isRightToLeftPar(buf->params))
1576 s << '.' << romanCounter(number);
1578 s << romanCounter(number) << '.';
1581 if (par->isRightToLeftPar(buf->params))
1583 << alphaCounter(number);
1585 s << alphaCounter(number)
1589 if (par->isRightToLeftPar(buf->params))
1596 par->params().labelString(s.str().c_str());
1598 for (i += par->enumdepth + 1; i < 10; ++i) {
1599 // reset the following counters
1600 par->setCounter(i, 0);
1604 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1605 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1607 int number = par->getCounter(i);
1609 InsetCommandParams p( "bibitem" );
1610 par->bibkey = new InsetBibKey(p);
1612 par->bibkey->setCounter(number);
1613 par->params().labelString(layout.labelstring());
1615 // In biblio should't be following counters but...
1617 string s = layout.labelstring();
1619 // the caption hack:
1620 if (layout.labeltype == LABEL_SENSITIVE) {
1621 bool isOK (par->inInset() && par->inInset()->owner() &&
1622 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1625 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1627 = floatList.getType(tmp->type());
1628 // We should get the correct number here too.
1629 s = fl.name() + " #:";
1631 /* par->SetLayout(0);
1632 s = layout->labelstring; */
1633 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1634 ? " :úåòîùî øñç" : "Senseless: ";
1637 par->params().labelString(s);
1639 /* reset the enumeration counter. They are always resetted
1640 * when there is any other layout between */
1641 for (int i = 6 + par->enumdepth; i < 10; ++i)
1642 par->setCounter(i, 0);
1647 // Updates all counters BEHIND the row. Changed paragraphs
1648 // with a dynamic left margin will be rebroken.
1649 void LyXText::updateCounters(BufferView * bview, Row * row) const
1657 par = row->par()->next();
1661 while (row->par() != par)
1664 setCounter(bview->buffer(), par);
1666 // now check for the headline layouts. remember that they
1667 // have a dynamic left margin
1668 if ((textclasslist.Style(bview->buffer()->params.textclass,
1669 par->layout).margintype == MARGIN_DYNAMIC
1670 || textclasslist.Style(bview->buffer()->params.textclass,
1671 par->layout).labeltype == LABEL_SENSITIVE)) {
1673 // Rebreak the paragraph
1674 removeParagraph(row);
1675 appendParagraph(bview, row);
1682 void LyXText::insertInset(BufferView * bview, Inset * inset)
1684 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1686 setUndo(bview, Undo::INSERT,
1687 cursor.par(), cursor.par()->next());
1688 cursor.par()->insertInset(cursor.pos(), inset);
1689 // Just to rebreak and refresh correctly.
1690 // The character will not be inserted a second time
1691 insertChar(bview, Paragraph::META_INSET);
1693 // If we enter a highly editable inset the cursor should be to before
1694 // the inset. This couldn't happen before as Undo was not handled inside
1695 // inset now after the Undo LyX tries to call inset->Edit(...) again
1696 // and cannot do this as the cursor is behind the inset and GetInset
1697 // does not return the inset!
1698 if (isHighlyEditableInset(inset)) {
1699 cursorLeft(bview, true);
1705 void LyXText::copyEnvironmentType()
1707 copylayouttype = cursor.par()->getLayout();
1711 void LyXText::pasteEnvironmentType(BufferView * bview)
1713 setLayout(bview, copylayouttype);
1717 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1719 // Stuff what we got on the clipboard. Even if there is no selection.
1721 // There is a problem with having the stuffing here in that the
1722 // larger the selection the slower LyX will get. This can be
1723 // solved by running the line below only when the selection has
1724 // finished. The solution used currently just works, to make it
1725 // faster we need to be more clever and probably also have more
1726 // calls to stuffClipboard. (Lgb)
1727 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1729 // This doesn't make sense, if there is no selection
1730 if (!selection.set())
1733 // OK, we have a selection. This is always between selection.start
1734 // and selection.end
1736 // make sure that the depth behind the selection are restored, too
1737 Paragraph * endpar = selection.end.par()->next();
1738 Paragraph * undoendpar = endpar;
1740 if (endpar && endpar->getDepth()) {
1741 while (endpar && endpar->getDepth()) {
1742 endpar = endpar->next();
1743 undoendpar = endpar;
1745 } else if (endpar) {
1746 endpar = endpar->next(); // because of parindents etc.
1749 setUndo(bview, Undo::DELETE,
1750 selection.start.par(), undoendpar);
1752 // there are two cases: cut only within one paragraph or
1753 // more than one paragraph
1754 if (selection.start.par() == selection.end.par()) {
1755 // only within one paragraph
1756 endpar = selection.end.par();
1757 int pos = selection.end.pos();
1758 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1759 selection.start.pos(), pos,
1760 bview->buffer()->params.textclass,
1762 selection.end.pos(pos);
1764 endpar = selection.end.par();
1765 int pos = selection.end.pos();
1766 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1767 selection.start.pos(), pos,
1768 bview->buffer()->params.textclass,
1771 selection.end.par(endpar);
1772 selection.end.pos(pos);
1773 cursor.pos(selection.end.pos());
1775 endpar = endpar->next();
1777 // sometimes necessary
1779 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1781 redoParagraphs(bview, selection.start, endpar);
1783 // cutSelection can invalidate the cursor so we need to set
1785 cursor = selection.start;
1787 // need a valid cursor. (Lgb)
1790 setCursor(bview, cursor.par(), cursor.pos());
1791 selection.cursor = cursor;
1792 updateCounters(bview, cursor.row());
1796 void LyXText::copySelection(BufferView * bview)
1798 // Stuff what we got on the clipboard. Even if there is no selection.
1800 // There is a problem with having the stuffing here in that the
1801 // larger the selection the slower LyX will get. This can be
1802 // solved by running the line below only when the selection has
1803 // finished. The solution used currently just works, to make it
1804 // faster we need to be more clever and probably also have more
1805 // calls to stuffClipboard. (Lgb)
1806 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1808 // this doesnt make sense, if there is no selection
1809 if (!selection.set())
1812 // ok we have a selection. This is always between selection.start
1813 // and sel_end cursor
1815 // copy behind a space if there is one
1816 while (selection.start.par()->size() > selection.start.pos()
1817 && selection.start.par()->isLineSeparator(selection.start.pos())
1818 && (selection.start.par() != selection.end.par()
1819 || selection.start.pos() < selection.end.pos()))
1820 selection.start.pos(selection.start.pos() + 1);
1822 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1823 selection.start.pos(), selection.end.pos(),
1824 bview->buffer()->params.textclass);
1828 void LyXText::pasteSelection(BufferView * bview)
1830 // this does not make sense, if there is nothing to paste
1831 if (!CutAndPaste::checkPastePossible(cursor.par()))
1834 setUndo(bview, Undo::INSERT,
1835 cursor.par(), cursor.par()->next());
1838 Paragraph * actpar = cursor.par();
1839 int pos = cursor.pos();
1841 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1842 bview->buffer()->params.textclass);
1844 redoParagraphs(bview, cursor, endpar);
1846 setCursor(bview, cursor.par(), cursor.pos());
1849 setCursor(bview, actpar, pos);
1850 updateCounters(bview, cursor.row());
1854 // returns a pointer to the very first Paragraph
1855 Paragraph * LyXText::firstParagraph() const
1857 return ownerParagraph();
1861 // sets the selection over the number of characters of string, no check!!
1862 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1867 selection.cursor = cursor;
1868 for (string::size_type i = 0; i < str.length(); ++i)
1870 setSelection(bview);
1874 // simple replacing. The font of the first selected character is used
1875 void LyXText::replaceSelectionWithString(BufferView * bview,
1878 setCursorParUndo(bview);
1881 if (!selection.set()) { // create a dummy selection
1882 selection.end = cursor;
1883 selection.start = cursor;
1886 // Get font setting before we cut
1887 Paragraph::size_type pos = selection.end.pos();
1888 LyXFont const font = selection.start.par()
1889 ->getFontSettings(bview->buffer()->params,
1890 selection.start.pos());
1892 // Insert the new string
1893 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1894 selection.end.par()->insertChar(pos, (*cit), font);
1898 // Cut the selection
1899 cutSelection(bview, true, false);
1905 // needed to insert the selection
1906 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1908 Paragraph * par = cursor.par();
1909 Paragraph::size_type pos = cursor.pos();
1910 Paragraph * endpar = cursor.par()->next();
1912 setCursorParUndo(bview);
1914 // only to be sure, should not be neccessary
1917 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1919 redoParagraphs(bview, cursor, endpar);
1920 setCursor(bview, cursor.par(), cursor.pos());
1921 selection.cursor = cursor;
1922 setCursor(bview, par, pos);
1923 setSelection(bview);
1927 // turns double-CR to single CR, others where converted into one
1928 // blank. Then InsertStringAsLines is called
1929 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1931 string linestr(str);
1932 bool newline_inserted = false;
1933 for (string::size_type i = 0; i < linestr.length(); ++i) {
1934 if (linestr[i] == '\n') {
1935 if (newline_inserted) {
1936 // we know that \r will be ignored by
1937 // InsertStringA. Of course, it is a dirty
1938 // trick, but it works...
1939 linestr[i - 1] = '\r';
1943 newline_inserted = true;
1945 } else if (IsPrintable(linestr[i])) {
1946 newline_inserted = false;
1949 insertStringAsLines(bview, linestr);
1953 bool LyXText::gotoNextInset(BufferView * bview,
1954 std::vector<Inset::Code> const & codes,
1955 string const & contents) const
1957 LyXCursor res = cursor;
1960 if (res.pos() < res.par()->size() - 1) {
1961 res.pos(res.pos() + 1);
1963 res.par(res.par()->next());
1967 } while (res.par() &&
1968 !(isMetaInset(res.par(), res.pos())
1969 && (inset = res.par()->getInset(res.pos())) != 0
1970 && find(codes.begin(), codes.end(), inset->lyxCode())
1972 && (contents.empty() ||
1973 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1977 setCursor(bview, res.par(), res.pos(), false);
1984 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1985 Paragraph::size_type pos)
1987 LyXCursor tmpcursor;
1990 Paragraph::size_type z;
1991 Row * row = getRow(par, pos, y);
1993 // is there a break one row above
1994 if (row->previous() && row->previous()->par() == row->par()) {
1995 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1996 if (z >= row->pos()) {
1997 // set the dimensions of the row above
1998 y -= row->previous()->height();
2000 refresh_row = row->previous();
2001 status(bview, LyXText::NEED_MORE_REFRESH);
2003 breakAgain(bview, row->previous());
2005 // set the cursor again. Otherwise
2006 // dangling pointers are possible
2007 setCursor(bview, cursor.par(), cursor.pos(),
2008 false, cursor.boundary());
2009 selection.cursor = cursor;
2014 int const tmpheight = row->height();
2015 Paragraph::size_type const tmplast = rowLast(row);
2019 breakAgain(bview, row);
2020 if (row->height() == tmpheight && rowLast(row) == tmplast)
2021 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2023 status(bview, LyXText::NEED_MORE_REFRESH);
2025 // check the special right address boxes
2026 if (textclasslist.Style(bview->buffer()->params.textclass,
2027 par->getLayout()).margintype
2028 == MARGIN_RIGHT_ADDRESS_BOX)
2036 redoDrawingOfParagraph(bview, tmpcursor);
2039 // set the cursor again. Otherwise dangling pointers are possible
2040 // also set the selection
2042 if (selection.set()) {
2044 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2045 false, selection.cursor.boundary());
2046 selection.cursor = cursor;
2047 setCursorIntern(bview, selection.start.par(),
2048 selection.start.pos(),
2049 false, selection.start.boundary());
2050 selection.start = cursor;
2051 setCursorIntern(bview, selection.end.par(),
2052 selection.end.pos(),
2053 false, selection.end.boundary());
2054 selection.end = cursor;
2055 setCursorIntern(bview, last_sel_cursor.par(),
2056 last_sel_cursor.pos(),
2057 false, last_sel_cursor.boundary());
2058 last_sel_cursor = cursor;
2061 setCursorIntern(bview, cursor.par(), cursor.pos(),
2062 false, cursor.boundary());
2066 // returns false if inset wasn't found
2067 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2069 // first check the current paragraph
2070 int pos = cursor.par()->getPositionOfInset(inset);
2072 checkParagraph(bview, cursor.par(), pos);
2076 // check every paragraph
2078 Paragraph * par = firstParagraph();
2080 pos = par->getPositionOfInset(inset);
2082 checkParagraph(bview, par, pos);
2092 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2093 Paragraph::size_type pos,
2094 bool setfont, bool boundary) const
2096 LyXCursor old_cursor = cursor;
2097 setCursorIntern(bview, par, pos, setfont, boundary);
2098 deleteEmptyParagraphMechanism(bview, old_cursor);
2102 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2103 Paragraph::size_type pos, bool boundary) const
2107 cur.boundary(boundary);
2109 // get the cursor y position in text
2111 Row * row = getRow(par, pos, y);
2112 // y is now the beginning of the cursor row
2113 y += row->baseline();
2114 // y is now the cursor baseline
2117 // now get the cursors x position
2119 float fill_separator;
2121 float fill_label_hfill;
2122 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2124 Paragraph::size_type cursor_vpos = 0;
2125 Paragraph::size_type last = rowLastPrintable(row);
2127 if (pos > last + 1) // This shouldn't happen.
2129 else if (pos < row->pos())
2132 if (last < row->pos())
2133 cursor_vpos = row->pos();
2134 else if (pos > last && !boundary)
2135 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2136 ? row->pos() : last + 1;
2137 else if (pos > row->pos() &&
2138 (pos > last || boundary))
2139 /// Place cursor after char at (logical) position pos - 1
2140 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2141 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2143 /// Place cursor before char at (logical) position pos
2144 cursor_vpos = (bidi_level(pos) % 2 == 0)
2145 ? log2vis(pos) : log2vis(pos) + 1;
2147 Paragraph::size_type main_body =
2148 beginningOfMainBody(bview->buffer(), row->par());
2149 if ((main_body > 0) &&
2150 ((main_body-1 > last) ||
2151 !row->par()->isLineSeparator(main_body-1)))
2154 for (Paragraph::size_type vpos = row->pos();
2155 vpos < cursor_vpos; ++vpos) {
2156 pos = vis2log(vpos);
2157 if (main_body > 0 && pos == main_body - 1) {
2158 x += fill_label_hfill +
2159 lyxfont::width(textclasslist.Style(
2160 bview->buffer()->params.textclass,
2161 row->par()->getLayout())
2163 getLabelFont(bview->buffer(), row->par()));
2164 if (row->par()->isLineSeparator(main_body-1))
2165 x -= singleWidth(bview, row->par(),main_body-1);
2167 if (hfillExpansion(bview->buffer(), row, pos)) {
2168 x += singleWidth(bview, row->par(), pos);
2169 if (pos >= main_body)
2172 x += fill_label_hfill;
2173 } else if (row->par()->isSeparator(pos)) {
2174 x += singleWidth(bview, row->par(), pos);
2175 if (pos >= main_body)
2176 x += fill_separator;
2178 x += singleWidth(bview, row->par(), pos);
2187 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2188 Paragraph::size_type pos,
2189 bool setfont, bool boundary) const
2191 InsetText * it = static_cast<InsetText *>(par->inInset());
2193 if (it != inset_owner) {
2194 lyxerr << "InsetText is " << it << endl;
2195 lyxerr << "inset_owner is " << inset_owner << endl;
2196 #warning I believe this code is wrong. (Lgb)
2197 #warning Jürgen, have a look at this. (Lgb)
2198 #warning Hmmm, I guess you are right but we
2199 #warning should verify when this is needed
2200 // Jürgen, would you like to have a look?
2201 // I guess we need to move the outer cursor
2202 // and open and lock the inset (bla bla bla)
2203 // stuff I don't know... so can you have a look?
2205 // I moved the lyxerr stuff in here so we can see if
2206 // this is actually really needed and where!
2208 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2214 setCursor(bview, cursor, par, pos, boundary);
2216 setCurrentFont(bview);
2220 void LyXText::setCurrentFont(BufferView * bview) const
2222 Paragraph::size_type pos = cursor.pos();
2223 if (cursor.boundary() && pos > 0)
2227 if (pos == cursor.par()->size())
2229 else // potentional bug... BUG (Lgb)
2230 if (cursor.par()->isSeparator(pos)) {
2231 if (pos > cursor.row()->pos() &&
2232 bidi_level(pos) % 2 ==
2233 bidi_level(pos - 1) % 2)
2235 else if (pos + 1 < cursor.par()->size())
2241 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2242 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2244 if (cursor.pos() == cursor.par()->size() &&
2245 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2246 !cursor.boundary()) {
2247 Language const * lang =
2248 cursor.par()->getParLanguage(bview->buffer()->params);
2249 current_font.setLanguage(lang);
2250 current_font.setNumber(LyXFont::OFF);
2251 real_current_font.setLanguage(lang);
2252 real_current_font.setNumber(LyXFont::OFF);
2257 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2259 LyXCursor old_cursor = cursor;
2261 setCursorFromCoordinates(bview, cursor, x, y);
2262 setCurrentFont(bview);
2263 deleteEmptyParagraphMechanism(bview, old_cursor);
2267 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2270 // Get the row first.
2272 Row * row = getRowNearY(y);
2274 Paragraph::size_type const column = getColumnNearX(bview, row, x,
2277 cur.par(row->par());
2278 cur.pos(row->pos() + column);
2280 cur.y(y + row->baseline());
2282 cur.boundary(bound);
2286 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2288 if (cursor.pos() > 0) {
2289 bool boundary = cursor.boundary();
2290 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2291 if (!internal && !boundary &&
2292 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2293 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2294 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2295 Paragraph * par = cursor.par()->previous();
2296 setCursor(bview, par, par->size());
2301 void LyXText::cursorRight(BufferView * bview, bool internal) const
2303 if (!internal && cursor.boundary() &&
2304 !cursor.par()->isNewline(cursor.pos()))
2305 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2306 else if (cursor.pos() < cursor.par()->size()) {
2307 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2309 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2310 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2311 } else if (cursor.par()->next())
2312 setCursor(bview, cursor.par()->next(), 0);
2316 void LyXText::cursorUp(BufferView * bview) const
2318 setCursorFromCoordinates(bview, cursor.x_fix(),
2319 cursor.y() - cursor.row()->baseline() - 1);
2323 void LyXText::cursorDown(BufferView * bview) const
2325 setCursorFromCoordinates(bview, cursor.x_fix(),
2326 cursor.y() - cursor.row()->baseline()
2327 + cursor.row()->height() + 1);
2331 void LyXText::cursorUpParagraph(BufferView * bview) const
2333 if (cursor.pos() > 0) {
2334 setCursor(bview, cursor.par(), 0);
2336 else if (cursor.par()->previous()) {
2337 setCursor(bview, cursor.par()->previous(), 0);
2342 void LyXText::cursorDownParagraph(BufferView * bview) const
2344 if (cursor.par()->next()) {
2345 setCursor(bview, cursor.par()->next(), 0);
2347 setCursor(bview, cursor.par(), cursor.par()->size());
2351 // fix the cursor `cur' after a characters has been deleted at `where'
2352 // position. Called by deleteEmptyParagraphMechanism
2353 void LyXText::fixCursorAfterDelete(BufferView * bview,
2355 LyXCursor const & where) const
2357 // if cursor is not in the paragraph where the delete occured,
2359 if (cur.par() != where.par())
2362 // if cursor position is after the place where the delete occured,
2364 if (cur.pos() > where.pos())
2365 cur.pos(cur.pos()-1);
2367 // recompute row et al. for this cursor
2368 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2372 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2373 LyXCursor const & old_cursor) const
2375 // Would be wrong to delete anything if we have a selection.
2376 if (selection.set()) return;
2378 // We allow all kinds of "mumbo-jumbo" when freespacing.
2379 if (textclasslist.Style(bview->buffer()->params.textclass,
2380 old_cursor.par()->getLayout()).free_spacing)
2383 bool deleted = false;
2385 /* Ok I'll put some comments here about what is missing.
2386 I have fixed BackSpace (and thus Delete) to not delete
2387 double-spaces automagically. I have also changed Cut,
2388 Copy and Paste to hopefully do some sensible things.
2389 There are still some small problems that can lead to
2390 double spaces stored in the document file or space at
2391 the beginning of paragraphs. This happens if you have
2392 the cursor betwenn to spaces and then save. Or if you
2393 cut and paste and the selection have a space at the
2394 beginning and then save right after the paste. I am
2395 sure none of these are very hard to fix, but I will
2396 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2397 that I can get some feedback. (Lgb)
2400 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2401 // delete the LineSeparator.
2404 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2405 // delete the LineSeparator.
2408 // If the pos around the old_cursor were spaces, delete one of them.
2409 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2410 // Only if the cursor has really moved
2412 if (old_cursor.pos() > 0
2413 && old_cursor.pos() < old_cursor.par()->size()
2414 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2415 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2416 old_cursor.par()->erase(old_cursor.pos() - 1);
2417 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2419 #ifdef WITH_WARNINGS
2420 #warning This will not work anymore when we have multiple views of the same buffer
2421 // In this case, we will have to correct also the cursors held by
2422 // other bufferviews. It will probably be easier to do that in a more
2423 // automated way in LyXCursor code. (JMarc 26/09/2001)
2425 // correct all cursors held by the LyXText
2426 fixCursorAfterDelete(bview, cursor, old_cursor);
2427 fixCursorAfterDelete(bview, selection.cursor,
2429 fixCursorAfterDelete(bview, selection.start,
2431 fixCursorAfterDelete(bview, selection.end, old_cursor);
2432 fixCursorAfterDelete(bview, last_sel_cursor,
2434 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2435 fixCursorAfterDelete(bview, toggle_end_cursor,
2441 // Do not delete empty paragraphs with keepempty set.
2442 if ((textclasslist.Style(bview->buffer()->params.textclass,
2443 old_cursor.par()->getLayout())).keepempty)
2446 // only do our magic if we changed paragraph
2447 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
2454 LyXCursor tmpcursor;
2456 // make sure that you do not delete any environments
2457 status(bview, LyXText::NEED_MORE_REFRESH);
2460 if (old_cursor.row()->previous()) {
2461 refresh_row = old_cursor.row()->previous();
2462 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2464 cursor = old_cursor; // that undo can restore the right cursor position
2465 Paragraph * endpar = old_cursor.par()->next();
2466 if (endpar && endpar->getDepth()) {
2467 while (endpar && endpar->getDepth()) {
2468 endpar = endpar->next();
2471 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2475 removeRow(old_cursor.row());
2476 if (ownerParagraph() == old_cursor.par()) {
2477 ownerParagraph(ownerParagraph()->next());
2480 delete old_cursor.par();
2482 /* Breakagain the next par. Needed because of
2483 * the parindent that can occur or dissappear.
2484 * The next row can change its height, if
2485 * there is another layout before */
2486 if (refresh_row->next()) {
2487 breakAgain(bview, refresh_row->next());
2488 updateCounters(bview, refresh_row);
2490 setHeightOfRow(bview, refresh_row);
2492 refresh_row = old_cursor.row()->next();
2493 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2496 cursor = old_cursor; // that undo can restore the right cursor position
2497 Paragraph * endpar = old_cursor.par()->next();
2498 if (endpar && endpar->getDepth()) {
2499 while (endpar && endpar->getDepth()) {
2500 endpar = endpar->next();
2503 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2507 removeRow(old_cursor.row());
2509 if (ownerParagraph() == old_cursor.par()) {
2510 ownerParagraph(ownerParagraph()->next());
2513 delete old_cursor.par();
2515 /* Breakagain the next par. Needed because of
2516 the parindent that can occur or dissappear.
2517 The next row can change its height, if
2518 there is another layout before */
2520 breakAgain(bview, refresh_row);
2521 updateCounters(bview, refresh_row->previous());
2526 setCursorIntern(bview, cursor.par(), cursor.pos());
2528 if (selection.cursor.par() == old_cursor.par()
2529 && selection.cursor.pos() == selection.cursor.pos()) {
2530 // correct selection
2531 selection.cursor = cursor;
2535 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2536 redoParagraphs(bview, old_cursor,
2537 old_cursor.par()->next());
2539 setCursorIntern(bview, cursor.par(), cursor.pos());
2540 selection.cursor = cursor;
2546 void LyXText::toggleAppendix(BufferView * bview)
2548 Paragraph * par = cursor.par();
2549 bool start = !par->params().startOfAppendix();
2551 // ensure that we have only one start_of_appendix in this document
2552 Paragraph * tmp = firstParagraph();
2553 for (; tmp; tmp = tmp->next()) {
2554 tmp->params().startOfAppendix(false);
2557 par->params().startOfAppendix(start);
2559 // we can set the refreshing parameters now
2560 status(bview, LyXText::NEED_MORE_REFRESH);
2562 refresh_row = 0; // not needed for full update
2563 updateCounters(bview, 0);
2564 setCursor(bview, cursor.par(), cursor.pos());
2568 Paragraph * LyXText::ownerParagraph() const
2571 return inset_owner->paragraph();
2573 return bv_owner->buffer()->paragraph;
2577 void LyXText::ownerParagraph(Paragraph * p) const
2580 inset_owner->paragraph(p);
2582 bv_owner->buffer()->paragraph = p;
2587 void LyXText::ownerParagraph(int id, Paragraph * p) const
2589 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2590 if (op && op->inInset()) {
2591 static_cast<InsetText *>(op->inInset())->paragraph(p);
2594 inset_owner->paragraph(p);
2596 bv_owner->buffer()->paragraph = p;
2602 LyXText::text_status LyXText::status() const
2608 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2610 // well as much as I know && binds more then || so the above and the
2611 // below are identical (this for your known use of parentesis!)
2612 // Now some explanation:
2613 // We should only go up with refreshing code so this means that if
2614 // we have a MORE refresh we should never set it to LITTLE if we still
2615 // didn't handle it (and then it will be UNCHANGED. Now as long as
2616 // we stay inside one LyXText this may work but we need to tell the
2617 // outermost LyXText that it should REALLY draw us if there is some
2618 // change in a Inset::LyXText. So you see that when we are inside a
2619 // inset's LyXText we give the LITTLE to the outermost LyXText to
2620 // tell'em that it should redraw the actual row (where the inset
2621 // resides! Capito?!
2623 if ((status_ != NEED_MORE_REFRESH)
2624 || (status_ == NEED_MORE_REFRESH
2625 && st != NEED_VERY_LITTLE_REFRESH))
2628 if (inset_owner && st != UNCHANGED) {
2629 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2630 if (!bview->text->refresh_row) {
2631 bview->text->refresh_row = bview->text->cursor.row();
2632 bview->text->refresh_y = bview->text->cursor.y() -
2633 bview->text->cursor.row()->baseline();