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->isInset(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
389 row->next()->previous(row->previous());
390 if (!row->previous()) {
391 firstrow = row->next();
393 row->previous()->next(row->next());
396 lastrow = row->previous();
398 height -= row->height(); // the text becomes smaller
401 --number_of_rows; // one row less
405 // remove all following rows of the paragraph of the specified row.
406 void LyXText::removeParagraph(Row * row) const
408 Paragraph * tmppar = row->par();
412 while (row && row->par() == tmppar) {
413 tmprow = row->next();
420 // insert the specified paragraph behind the specified row
421 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
424 insertRow(row, par, 0); /* insert a new row, starting
427 setCounter(bview->buffer(), par); // set the counters
429 // and now append the whole paragraph behind the new row
432 appendParagraph(bview, firstrow);
434 row->next()->height(0);
435 appendParagraph(bview, row->next());
440 Inset * LyXText::getInset() const
443 if (cursor.pos() == 0 && cursor.par()->bibkey) {
444 inset = cursor.par()->bibkey;
445 } else if (cursor.pos() < cursor.par()->size()
446 && cursor.par()->isInset(cursor.pos())) {
447 inset = cursor.par()->getInset(cursor.pos());
453 void LyXText::toggleInset(BufferView * bview)
455 Inset * inset = getInset();
456 if (!isEditableInset(inset))
458 //bview->owner()->message(inset->editMessage());
460 // do we want to keep this?? (JMarc)
461 if (!isHighlyEditableInset(inset))
462 setCursorParUndo(bview);
464 if (inset->isOpen()) {
470 inset->open(bview, !inset->isOpen());
475 /* used in setlayout */
476 // Asger is not sure we want to do this...
477 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
480 LyXLayout const & layout =
481 textclasslist.Style(buf->params.textclass, par->getLayout());
484 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
485 if (pos < beginningOfMainBody(buf, par))
486 layoutfont = layout.labelfont;
488 layoutfont = layout.font;
490 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
491 tmpfont.reduce(layoutfont);
492 par->setFont(pos, tmpfont);
497 Paragraph * LyXText::setLayout(BufferView * bview,
498 LyXCursor & cur, LyXCursor & sstart_cur,
499 LyXCursor & send_cur,
500 LyXTextClass::size_type layout)
502 Paragraph * endpar = send_cur.par()->next();
503 Paragraph * undoendpar = endpar;
505 if (endpar && endpar->getDepth()) {
506 while (endpar && endpar->getDepth()) {
507 endpar = endpar->next();
511 endpar = endpar->next(); // because of parindents etc.
514 setUndo(bview, Undo::EDIT,
515 sstart_cur.par(), undoendpar);
517 // ok we have a selection. This is always between sstart_cur
518 // and sel_end cursor
521 LyXLayout const & lyxlayout =
522 textclasslist.Style(bview->buffer()->params.textclass, layout);
524 while (cur.par() != send_cur.par()) {
525 cur.par()->setLayout(layout);
526 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
527 Paragraph * fppar = cur.par();
528 fppar->params().spaceTop(lyxlayout.fill_top ?
529 VSpace(VSpace::VFILL)
530 : VSpace(VSpace::NONE));
531 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
532 VSpace(VSpace::VFILL)
533 : VSpace(VSpace::NONE));
534 if (lyxlayout.margintype == MARGIN_MANUAL)
535 cur.par()->setLabelWidthString(lyxlayout.labelstring());
536 if (lyxlayout.labeltype != LABEL_BIBLIO
538 delete fppar->bibkey;
541 cur.par(cur.par()->next());
543 cur.par()->setLayout(layout);
544 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
545 Paragraph * fppar = cur.par();
546 fppar->params().spaceTop(lyxlayout.fill_top ?
547 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
548 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
549 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
550 if (lyxlayout.margintype == MARGIN_MANUAL)
551 cur.par()->setLabelWidthString(lyxlayout.labelstring());
552 if (lyxlayout.labeltype != LABEL_BIBLIO
554 delete fppar->bibkey;
561 // set layout over selection and make a total rebreak of those paragraphs
562 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
564 LyXCursor tmpcursor = cursor; /* store the current cursor */
566 // if there is no selection just set the layout
567 // of the current paragraph */
568 if (!selection.set()) {
569 selection.start = cursor; // dummy selection
570 selection.end = cursor;
572 Paragraph * endpar = setLayout(bview, cursor, selection.start,
573 selection.end, layout);
574 redoParagraphs(bview, selection.start, endpar);
576 // we have to reset the selection, because the
577 // geometry could have changed
578 setCursor(bview, selection.start.par(),
579 selection.start.pos(), false);
580 selection.cursor = cursor;
581 setCursor(bview, selection.end.par(), selection.end.pos(), false);
582 updateCounters(bview, cursor.row());
585 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
589 // increment depth over selection and
590 // make a total rebreak of those paragraphs
591 void LyXText::incDepth(BufferView * bview)
593 // If there is no selection, just use the current paragraph
594 if (!selection.set()) {
595 selection.start = cursor; // dummy selection
596 selection.end = cursor;
599 // We end at the next paragraph with depth 0
600 Paragraph * endpar = selection.end.par()->next();
602 Paragraph * undoendpar = endpar;
604 if (endpar && endpar->getDepth()) {
605 while (endpar && endpar->getDepth()) {
606 endpar = endpar->next();
610 endpar = endpar->next(); // because of parindents etc.
613 setUndo(bview, Undo::EDIT,
614 selection.start.par(), undoendpar);
616 LyXCursor tmpcursor = cursor; // store the current cursor
618 // ok we have a selection. This is always between sel_start_cursor
619 // and sel_end cursor
620 cursor = selection.start;
622 bool anything_changed = false;
625 // NOTE: you can't change the depth of a bibliography entry
627 textclasslist.Style(bview->buffer()->params.textclass,
628 cursor.par()->getLayout()
629 ).labeltype != LABEL_BIBLIO) {
630 Paragraph * prev = cursor.par()->previous();
633 && (prev->getDepth() - cursor.par()->getDepth() > 0
634 || (prev->getDepth() == cursor.par()->getDepth()
635 && textclasslist.Style(bview->buffer()->params.textclass,
636 prev->getLayout()).isEnvironment()))) {
637 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
638 anything_changed = true;
641 if (cursor.par() == selection.end.par())
643 cursor.par(cursor.par()->next());
646 // if nothing changed set all depth to 0
647 if (!anything_changed) {
648 cursor = selection.start;
649 while (cursor.par() != selection.end.par()) {
650 cursor.par()->params().depth(0);
651 cursor.par(cursor.par()->next());
653 cursor.par()->params().depth(0);
656 redoParagraphs(bview, selection.start, endpar);
658 // we have to reset the selection, because the
659 // geometry could have changed
660 setCursor(bview, selection.start.par(), selection.start.pos());
661 selection.cursor = cursor;
662 setCursor(bview, selection.end.par(), selection.end.pos());
663 updateCounters(bview, cursor.row());
666 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
670 // decrement depth over selection and
671 // make a total rebreak of those paragraphs
672 void LyXText::decDepth(BufferView * bview)
674 // if there is no selection just set the layout
675 // of the current paragraph
676 if (!selection.set()) {
677 selection.start = cursor; // dummy selection
678 selection.end = cursor;
680 Paragraph * endpar = selection.end.par()->next();
681 Paragraph * undoendpar = endpar;
683 if (endpar && endpar->getDepth()) {
684 while (endpar && endpar->getDepth()) {
685 endpar = endpar->next();
689 endpar = endpar->next(); // because of parindents etc.
692 setUndo(bview, Undo::EDIT,
693 selection.start.par(), undoendpar);
695 LyXCursor tmpcursor = cursor; // store the current cursor
697 // ok we have a selection. This is always between sel_start_cursor
698 // and sel_end cursor
699 cursor = selection.start;
702 if (cursor.par()->params().depth()) {
703 cursor.par()->params()
704 .depth(cursor.par()->params().depth() - 1);
706 if (cursor.par() == selection.end.par()) {
709 cursor.par(cursor.par()->next());
712 redoParagraphs(bview, selection.start, endpar);
714 // we have to reset the selection, because the
715 // geometry could have changed
716 setCursor(bview, selection.start.par(),
717 selection.start.pos());
718 selection.cursor = cursor;
719 setCursor(bview, selection.end.par(), selection.end.pos());
720 updateCounters(bview, cursor.row());
723 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
727 // set font over selection and make a total rebreak of those paragraphs
728 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
730 // if there is no selection just set the current_font
731 if (!selection.set()) {
732 // Determine basis font
734 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
736 layoutfont = getLabelFont(bview->buffer(),
739 layoutfont = getLayoutFont(bview->buffer(),
742 // Update current font
743 real_current_font.update(font,
744 bview->buffer()->params.language,
747 // Reduce to implicit settings
748 current_font = real_current_font;
749 current_font.reduce(layoutfont);
750 // And resolve it completely
751 #ifndef INHERIT_LANGUAGE
752 real_current_font.realize(layoutfont);
754 real_current_font.realize(layoutfont,
755 bview->buffer()->params.language);
760 LyXCursor tmpcursor = cursor; // store the current cursor
762 // ok we have a selection. This is always between sel_start_cursor
763 // and sel_end cursor
765 setUndo(bview, Undo::EDIT,
766 selection.start.par(), selection.end.par()->next());
768 cursor = selection.start;
769 while (cursor.par() != selection.end.par() ||
770 (cursor.pos() < selection.end.pos()))
772 if (cursor.pos() < cursor.par()->size()) {
773 // an open footnote should behave
775 setCharFont(bview, cursor.par(), cursor.pos(),
777 cursor.pos(cursor.pos() + 1);
780 cursor.par(cursor.par()->next());
785 redoParagraphs(bview, selection.start, selection.end.par()->next());
787 // we have to reset the selection, because the
788 // geometry could have changed
789 setCursor(bview, selection.start.par(), selection.start.pos());
790 selection.cursor = cursor;
791 setCursor(bview, selection.end.par(), selection.end.pos());
794 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
795 tmpcursor.boundary());
799 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
801 Row * tmprow = cur.row();
802 int y = cur.y() - tmprow->baseline();
804 setHeightOfRow(bview, tmprow);
806 while (tmprow->previous()
807 && tmprow->previous()->par() == tmprow->par()) {
808 tmprow = tmprow->previous();
809 y -= tmprow->height();
810 setHeightOfRow(bview, tmprow);
813 // we can set the refreshing parameters now
814 status(bview, LyXText::NEED_MORE_REFRESH);
816 refresh_row = tmprow;
817 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
821 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
823 Row * tmprow = cur.row();
825 int y = cur.y() - tmprow->baseline();
826 setHeightOfRow(bview, tmprow);
828 while (tmprow->previous()
829 && tmprow->previous()->par() == tmprow->par()) {
830 tmprow = tmprow->previous();
831 y -= tmprow->height();
834 // we can set the refreshing parameters now
835 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
837 refresh_row = tmprow;
839 status(bview, LyXText::NEED_MORE_REFRESH);
840 setCursor(bview, cur.par(), cur.pos());
844 // deletes and inserts again all paragaphs between the cursor
845 // and the specified par
846 // This function is needed after SetLayout and SetFont etc.
847 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
848 Paragraph const * endpar) const
851 Paragraph * tmppar = 0;
852 Paragraph * first_phys_par = 0;
854 Row * tmprow = cur.row();
856 int y = cur.y() - tmprow->baseline();
858 if (!tmprow->previous()) {
859 // a trick/hack for UNDO
860 // Can somebody please tell me _why_ this solves
862 first_phys_par = firstParagraph();
864 first_phys_par = tmprow->par();
865 while (tmprow->previous()
866 && tmprow->previous()->par() == first_phys_par)
868 tmprow = tmprow->previous();
869 y -= tmprow->height();
873 // we can set the refreshing parameters now
874 status(bview, LyXText::NEED_MORE_REFRESH);
876 refresh_row = tmprow->previous(); /* the real refresh row will
877 be deleted, so I store
881 tmppar = tmprow->next()->par();
884 while (tmppar != endpar) {
885 removeRow(tmprow->next());
887 tmppar = tmprow->next()->par();
892 // remove the first one
893 tmprow2 = tmprow; /* this is because tmprow->previous()
895 tmprow = tmprow->previous();
898 tmppar = first_phys_par;
902 insertParagraph(bview, tmppar, tmprow);
906 while (tmprow->next()
907 && tmprow->next()->par() == tmppar) {
908 tmprow = tmprow->next();
910 tmppar = tmppar->next();
912 } while (tmppar && tmppar != endpar);
914 // this is because of layout changes
916 refresh_y -= refresh_row->height();
917 setHeightOfRow(bview, refresh_row);
919 refresh_row = firstrow;
921 setHeightOfRow(bview, refresh_row);
924 if (tmprow && tmprow->next())
925 setHeightOfRow(bview, tmprow->next());
929 bool LyXText::fullRebreak(BufferView * bview)
935 if (need_break_row) {
936 breakAgain(bview, need_break_row);
944 // important for the screen
947 /* the cursor set functions have a special mechanism. When they
948 * realize, that you left an empty paragraph, they will delete it.
949 * They also delete the corresponding row */
951 // need the selection cursor:
952 void LyXText::setSelection(BufferView * bview)
954 bool const lsel = selection.set();
956 if (!selection.set()) {
957 last_sel_cursor = selection.cursor;
958 selection.start = selection.cursor;
959 selection.end = selection.cursor;
964 // first the toggling area
965 if (cursor.y() < last_sel_cursor.y()
966 || (cursor.y() == last_sel_cursor.y()
967 && cursor.x() < last_sel_cursor.x())) {
968 toggle_end_cursor = last_sel_cursor;
969 toggle_cursor = cursor;
971 toggle_end_cursor = cursor;
972 toggle_cursor = last_sel_cursor;
975 last_sel_cursor = cursor;
977 // and now the whole selection
979 if (selection.cursor.par() == cursor.par())
980 if (selection.cursor.pos() < cursor.pos()) {
981 selection.end = cursor;
982 selection.start = selection.cursor;
984 selection.end = selection.cursor;
985 selection.start = cursor;
987 else if (selection.cursor.y() < cursor.y() ||
988 (selection.cursor.y() == cursor.y()
989 && selection.cursor.x() < cursor.x())) {
990 selection.end = cursor;
991 selection.start = selection.cursor;
994 selection.end = selection.cursor;
995 selection.start = cursor;
998 // a selection with no contents is not a selection
999 if (selection.start.par() == selection.end.par() &&
1000 selection.start.pos() == selection.end.pos())
1001 selection.set(false);
1003 if (inset_owner && (selection.set() || lsel))
1004 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1008 string const LyXText::selectionAsString(Buffer const * buffer,
1011 if (!selection.set()) return string();
1014 // Special handling if the whole selection is within one paragraph
1015 if (selection.start.par() == selection.end.par()) {
1016 result += selection.start.par()->asString(buffer,
1017 selection.start.pos(),
1018 selection.end.pos(),
1023 // The selection spans more than one paragraph
1025 // First paragraph in selection
1026 result += selection.start.par()->asString(buffer,
1027 selection.start.pos(),
1028 selection.start.par()->size(),
1032 // The paragraphs in between (if any)
1033 LyXCursor tmpcur(selection.start);
1034 tmpcur.par(tmpcur.par()->next());
1035 while (tmpcur.par() != selection.end.par()) {
1036 result += tmpcur.par()->asString(buffer, 0,
1037 tmpcur.par()->size(),
1039 tmpcur.par(tmpcur.par()->next());
1042 // Last paragraph in selection
1043 result += selection.end.par()->asString(buffer, 0,
1044 selection.end.pos(), label);
1050 void LyXText::clearSelection() const
1052 selection.set(false);
1053 selection.mark(false);
1054 selection.end = selection.start = selection.cursor = cursor;
1058 void LyXText::cursorHome(BufferView * bview) const
1060 setCursor(bview, cursor.par(), cursor.row()->pos());
1064 void LyXText::cursorEnd(BufferView * bview) const
1066 if (!cursor.row()->next()
1067 || cursor.row()->next()->par() != cursor.row()->par()) {
1068 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1070 if (cursor.par()->size() &&
1071 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1072 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1073 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1075 setCursor(bview,cursor.par(),
1076 rowLast(cursor.row()) + 1);
1082 void LyXText::cursorTop(BufferView * bview) const
1084 while (cursor.par()->previous())
1085 cursor.par(cursor.par()->previous());
1086 setCursor(bview, cursor.par(), 0);
1090 void LyXText::cursorBottom(BufferView * bview) const
1092 while (cursor.par()->next())
1093 cursor.par(cursor.par()->next());
1094 setCursor(bview, cursor.par(), cursor.par()->size());
1098 void LyXText::toggleFree(BufferView * bview,
1099 LyXFont const & font, bool toggleall)
1101 // If the mask is completely neutral, tell user
1102 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1103 // Could only happen with user style
1104 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1108 // Try implicit word selection
1109 // If there is a change in the language the implicit word selection
1111 LyXCursor resetCursor = cursor;
1112 bool implicitSelection = (font.language() == ignore_language
1113 && font.number() == LyXFont::IGNORE)
1114 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1117 setFont(bview, font, toggleall);
1119 // Implicit selections are cleared afterwards
1120 //and cursor is set to the original position.
1121 if (implicitSelection) {
1123 cursor = resetCursor;
1124 setCursor(bview, cursor.par(), cursor.pos());
1125 selection.cursor = cursor;
1128 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1132 string LyXText::getStringToIndex(BufferView * bview)
1136 // Try implicit word selection
1137 // If there is a change in the language the implicit word selection
1139 LyXCursor resetCursor = cursor;
1140 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1142 if (!selection.set()) {
1143 bview->owner()->message(_("Nothing to index!"));
1146 if (selection.start.par() != selection.end.par()) {
1147 bview->owner()->message(_("Cannot index more than one paragraph!"));
1151 idxstring = selectionAsString(bview->buffer(), false);
1153 // Implicit selections are cleared afterwards
1154 //and cursor is set to the original position.
1155 if (implicitSelection) {
1157 cursor = resetCursor;
1158 setCursor(bview, cursor.par(), cursor.pos());
1159 selection.cursor = cursor;
1165 Paragraph::size_type LyXText::beginningOfMainBody(Buffer const * buf,
1166 Paragraph const * par) const
1168 if (textclasslist.Style(buf->params.textclass,
1169 par->getLayout()).labeltype != LABEL_MANUAL)
1172 return par->beginningOfMainBody();
1176 /* the DTP switches for paragraphs. LyX will store them in the
1177 * first physicla paragraph. When a paragraph is broken, the top settings
1178 * rest, the bottom settings are given to the new one. So I can make shure,
1179 * they do not duplicate themself and you cannnot make dirty things with
1182 void LyXText::setParagraph(BufferView * bview,
1183 bool line_top, bool line_bottom,
1184 bool pagebreak_top, bool pagebreak_bottom,
1185 VSpace const & space_top,
1186 VSpace const & space_bottom,
1187 Spacing const & spacing,
1189 string labelwidthstring,
1192 LyXCursor tmpcursor = cursor;
1193 if (!selection.set()) {
1194 selection.start = cursor;
1195 selection.end = cursor;
1198 // make sure that the depth behind the selection are restored, too
1199 Paragraph * endpar = selection.end.par()->next();
1200 Paragraph * undoendpar = endpar;
1202 if (endpar && endpar->getDepth()) {
1203 while (endpar && endpar->getDepth()) {
1204 endpar = endpar->next();
1205 undoendpar = endpar;
1209 // because of parindents etc.
1210 endpar = endpar->next();
1213 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1216 Paragraph * tmppar = selection.end.par();
1217 while (tmppar != selection.start.par()->previous()) {
1218 setCursor(bview, tmppar, 0);
1219 status(bview, LyXText::NEED_MORE_REFRESH);
1220 refresh_row = cursor.row();
1221 refresh_y = cursor.y() - cursor.row()->baseline();
1222 cursor.par()->params().lineTop(line_top);
1223 cursor.par()->params().lineBottom(line_bottom);
1224 cursor.par()->params().pagebreakTop(pagebreak_top);
1225 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1226 cursor.par()->params().spaceTop(space_top);
1227 cursor.par()->params().spaceBottom(space_bottom);
1228 cursor.par()->params().spacing(spacing);
1229 // does the layout allow the new alignment?
1230 if (align == LYX_ALIGN_LAYOUT)
1231 align = textclasslist
1232 .Style(bview->buffer()->params.textclass,
1233 cursor.par()->getLayout()).align;
1234 if (align & textclasslist
1235 .Style(bview->buffer()->params.textclass,
1236 cursor.par()->getLayout()).alignpossible) {
1237 if (align == textclasslist
1238 .Style(bview->buffer()->params.textclass,
1239 cursor.par()->getLayout()).align)
1240 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1242 cursor.par()->params().align(align);
1244 cursor.par()->setLabelWidthString(labelwidthstring);
1245 cursor.par()->params().noindent(noindent);
1246 tmppar = cursor.par()->previous();
1249 redoParagraphs(bview, selection.start, endpar);
1252 setCursor(bview, selection.start.par(), selection.start.pos());
1253 selection.cursor = cursor;
1254 setCursor(bview, selection.end.par(), selection.end.pos());
1255 setSelection(bview);
1256 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1258 bview->updateInset(inset_owner, true);
1262 char loweralphaCounter(int n)
1264 if (n < 1 || n > 26)
1274 char alphaCounter(int n)
1276 if (n < 1 || n > 26)
1284 char hebrewCounter(int n)
1286 static const char hebrew[22] = {
1287 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1288 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1289 '÷', 'ø', 'ù', 'ú'
1291 if (n < 1 || n > 22)
1299 string const romanCounter(int n)
1301 static char const * roman[20] = {
1302 "i", "ii", "iii", "iv", "v",
1303 "vi", "vii", "viii", "ix", "x",
1304 "xi", "xii", "xiii", "xiv", "xv",
1305 "xvi", "xvii", "xviii", "xix", "xx"
1307 if (n < 1 || n > 20)
1316 // set the counter of a paragraph. This includes the labels
1317 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1319 LyXLayout const & layout =
1320 textclasslist.Style(buf->params.textclass,
1323 LyXTextClass const & textclass =
1324 textclasslist.TextClass(buf->params.textclass);
1326 // copy the prev-counters to this one,
1327 // unless this is the first paragraph
1328 if (par->previous()) {
1329 for (int i = 0; i < 10; ++i) {
1330 par->setCounter(i, par->previous()->getFirstCounter(i));
1332 par->params().appendix(par->previous()->params().appendix());
1333 if (!par->params().appendix() && par->params().startOfAppendix()) {
1334 par->params().appendix(true);
1335 for (int i = 0; i < 10; ++i) {
1336 par->setCounter(i, 0);
1339 par->enumdepth = par->previous()->enumdepth;
1340 par->itemdepth = par->previous()->itemdepth;
1342 for (int i = 0; i < 10; ++i) {
1343 par->setCounter(i, 0);
1345 par->params().appendix(par->params().startOfAppendix());
1350 /* Maybe we have to increment the enumeration depth.
1351 * BUT, enumeration in a footnote is considered in isolation from its
1352 * surrounding paragraph so don't increment if this is the
1353 * first line of the footnote
1354 * AND, bibliographies can't have their depth changed ie. they
1355 * are always of depth 0
1358 && par->previous()->getDepth() < par->getDepth()
1359 && textclasslist.Style(buf->params.textclass,
1360 par->previous()->getLayout()
1361 ).labeltype == LABEL_COUNTER_ENUMI
1362 && par->enumdepth < 3
1363 && layout.labeltype != LABEL_BIBLIO) {
1367 // Maybe we have to decrement the enumeration depth, see note above
1369 && par->previous()->getDepth() > par->getDepth()
1370 && layout.labeltype != LABEL_BIBLIO) {
1371 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1372 par->setCounter(6 + par->enumdepth,
1373 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1374 /* reset the counters.
1375 * A depth change is like a breaking layout
1377 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1378 par->setCounter(i, 0);
1381 if (!par->params().labelString().empty()) {
1382 par->params().labelString(string());
1385 if (layout.margintype == MARGIN_MANUAL) {
1386 if (par->params().labelWidthString().empty()) {
1387 par->setLabelWidthString(layout.labelstring());
1390 par->setLabelWidthString(string());
1393 // is it a layout that has an automatic label?
1394 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1396 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1397 if (i >= 0 && i<= buf->params.secnumdepth) {
1398 par->incCounter(i); // increment the counter
1400 // Is there a label? Useful for Chapter layout
1401 if (!par->params().appendix()) {
1402 if (!layout.labelstring().empty())
1403 par->params().labelString(layout.labelstring());
1405 par->params().labelString(string());
1407 if (!layout.labelstring_appendix().empty())
1408 par->params().labelString(layout.labelstring_appendix());
1410 par->params().labelString(string());
1415 if (!par->params().appendix()) {
1416 switch (2 * LABEL_COUNTER_CHAPTER -
1417 textclass.maxcounter() + i) {
1418 case LABEL_COUNTER_CHAPTER:
1419 s << par->getCounter(i);
1421 case LABEL_COUNTER_SECTION:
1422 s << par->getCounter(i - 1) << '.'
1423 << par->getCounter(i);
1425 case LABEL_COUNTER_SUBSECTION:
1426 s << par->getCounter(i - 2) << '.'
1427 << par->getCounter(i - 1) << '.'
1428 << par->getCounter(i);
1430 case LABEL_COUNTER_SUBSUBSECTION:
1431 s << par->getCounter(i - 3) << '.'
1432 << par->getCounter(i - 2) << '.'
1433 << par->getCounter(i - 1) << '.'
1434 << par->getCounter(i);
1437 case LABEL_COUNTER_PARAGRAPH:
1438 s << par->getCounter(i - 4) << '.'
1439 << par->getCounter(i - 3) << '.'
1440 << par->getCounter(i - 2) << '.'
1441 << par->getCounter(i - 1) << '.'
1442 << par->getCounter(i);
1444 case LABEL_COUNTER_SUBPARAGRAPH:
1445 s << par->getCounter(i - 5) << '.'
1446 << par->getCounter(i - 4) << '.'
1447 << par->getCounter(i - 3) << '.'
1448 << par->getCounter(i - 2) << '.'
1449 << par->getCounter(i - 1) << '.'
1450 << par->getCounter(i);
1454 // Can this ever be reached? And in the
1455 // case it is, how can this be correct?
1457 s << par->getCounter(i) << '.';
1460 } else { // appendix
1461 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1462 case LABEL_COUNTER_CHAPTER:
1463 if (par->isRightToLeftPar(buf->params))
1464 s << hebrewCounter(par->getCounter(i));
1466 s << alphaCounter(par->getCounter(i));
1468 case LABEL_COUNTER_SECTION:
1469 if (par->isRightToLeftPar(buf->params))
1470 s << hebrewCounter(par->getCounter(i - 1));
1472 s << alphaCounter(par->getCounter(i - 1));
1475 << par->getCounter(i);
1478 case LABEL_COUNTER_SUBSECTION:
1479 if (par->isRightToLeftPar(buf->params))
1480 s << hebrewCounter(par->getCounter(i - 2));
1482 s << alphaCounter(par->getCounter(i - 2));
1485 << par->getCounter(i-1) << '.'
1486 << par->getCounter(i);
1489 case LABEL_COUNTER_SUBSUBSECTION:
1490 if (par->isRightToLeftPar(buf->params))
1491 s << hebrewCounter(par->getCounter(i-3));
1493 s << alphaCounter(par->getCounter(i-3));
1496 << par->getCounter(i-2) << '.'
1497 << par->getCounter(i-1) << '.'
1498 << par->getCounter(i);
1501 case LABEL_COUNTER_PARAGRAPH:
1502 if (par->isRightToLeftPar(buf->params))
1503 s << hebrewCounter(par->getCounter(i-4));
1505 s << alphaCounter(par->getCounter(i-4));
1508 << par->getCounter(i-3) << '.'
1509 << par->getCounter(i-2) << '.'
1510 << par->getCounter(i-1) << '.'
1511 << par->getCounter(i);
1514 case LABEL_COUNTER_SUBPARAGRAPH:
1515 if (par->isRightToLeftPar(buf->params))
1516 s << hebrewCounter(par->getCounter(i-5));
1518 s << alphaCounter(par->getCounter(i-5));
1521 << par->getCounter(i-4) << '.'
1522 << par->getCounter(i-3) << '.'
1523 << par->getCounter(i-2) << '.'
1524 << par->getCounter(i-1) << '.'
1525 << par->getCounter(i);
1529 // Can this ever be reached? And in the
1530 // case it is, how can this be correct?
1532 s << par->getCounter(i) << '.';
1538 par->params().labelString(par->params().labelString() +s.str().c_str());
1539 // We really want to remove the c_str as soon as
1542 for (i++; i < 10; ++i) {
1543 // reset the following counters
1544 par->setCounter(i, 0);
1546 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1547 for (i++; i < 10; ++i) {
1548 // reset the following counters
1549 par->setCounter(i, 0);
1551 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1552 par->incCounter(i + par->enumdepth);
1553 int number = par->getCounter(i + par->enumdepth);
1557 switch (par->enumdepth) {
1559 if (par->isRightToLeftPar(buf->params))
1561 << hebrewCounter(number)
1565 << loweralphaCounter(number)
1569 if (par->isRightToLeftPar(buf->params))
1570 s << '.' << romanCounter(number);
1572 s << romanCounter(number) << '.';
1575 if (par->isRightToLeftPar(buf->params))
1577 << alphaCounter(number);
1579 s << alphaCounter(number)
1583 if (par->isRightToLeftPar(buf->params))
1590 par->params().labelString(s.str().c_str());
1592 for (i += par->enumdepth + 1; i < 10; ++i) {
1593 // reset the following counters
1594 par->setCounter(i, 0);
1598 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1599 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1601 int number = par->getCounter(i);
1603 InsetCommandParams p( "bibitem" );
1604 par->bibkey = new InsetBibKey(p);
1606 par->bibkey->setCounter(number);
1607 par->params().labelString(layout.labelstring());
1609 // In biblio should't be following counters but...
1611 string s = layout.labelstring();
1613 // the caption hack:
1614 if (layout.labeltype == LABEL_SENSITIVE) {
1615 bool isOK (par->inInset() && par->inInset()->owner() &&
1616 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1619 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1621 = floatList.getType(tmp->type());
1622 // We should get the correct number here too.
1623 s = fl.name() + " #:";
1625 /* par->SetLayout(0);
1626 s = layout->labelstring; */
1627 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1628 ? " :úåòîùî øñç" : "Senseless: ";
1631 par->params().labelString(s);
1633 /* reset the enumeration counter. They are always resetted
1634 * when there is any other layout between */
1635 for (int i = 6 + par->enumdepth; i < 10; ++i)
1636 par->setCounter(i, 0);
1641 // Updates all counters BEHIND the row. Changed paragraphs
1642 // with a dynamic left margin will be rebroken.
1643 void LyXText::updateCounters(BufferView * bview, Row * row) const
1651 par = row->par()->next();
1655 while (row->par() != par)
1658 setCounter(bview->buffer(), par);
1660 // now check for the headline layouts. remember that they
1661 // have a dynamic left margin
1662 if ((textclasslist.Style(bview->buffer()->params.textclass,
1663 par->layout).margintype == MARGIN_DYNAMIC
1664 || textclasslist.Style(bview->buffer()->params.textclass,
1665 par->layout).labeltype == LABEL_SENSITIVE)) {
1667 // Rebreak the paragraph
1668 removeParagraph(row);
1669 appendParagraph(bview, row);
1676 void LyXText::insertInset(BufferView * bview, Inset * inset)
1678 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1680 setUndo(bview, Undo::INSERT,
1681 cursor.par(), cursor.par()->next());
1682 cursor.par()->insertInset(cursor.pos(), inset);
1683 // Just to rebreak and refresh correctly.
1684 // The character will not be inserted a second time
1685 insertChar(bview, Paragraph::META_INSET);
1687 // If we enter a highly editable inset the cursor should be to before
1688 // the inset. This couldn't happen before as Undo was not handled inside
1689 // inset now after the Undo LyX tries to call inset->Edit(...) again
1690 // and cannot do this as the cursor is behind the inset and GetInset
1691 // does not return the inset!
1692 if (isHighlyEditableInset(inset)) {
1693 cursorLeft(bview, true);
1699 void LyXText::copyEnvironmentType()
1701 copylayouttype = cursor.par()->getLayout();
1705 void LyXText::pasteEnvironmentType(BufferView * bview)
1707 setLayout(bview, copylayouttype);
1711 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1713 // Stuff what we got on the clipboard. Even if there is no selection.
1715 // There is a problem with having the stuffing here in that the
1716 // larger the selection the slower LyX will get. This can be
1717 // solved by running the line below only when the selection has
1718 // finished. The solution used currently just works, to make it
1719 // faster we need to be more clever and probably also have more
1720 // calls to stuffClipboard. (Lgb)
1721 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1723 // This doesn't make sense, if there is no selection
1724 if (!selection.set())
1727 // OK, we have a selection. This is always between selection.start
1728 // and selection.end
1730 // make sure that the depth behind the selection are restored, too
1731 Paragraph * endpar = selection.end.par()->next();
1732 Paragraph * undoendpar = endpar;
1734 if (endpar && endpar->getDepth()) {
1735 while (endpar && endpar->getDepth()) {
1736 endpar = endpar->next();
1737 undoendpar = endpar;
1739 } else if (endpar) {
1740 endpar = endpar->next(); // because of parindents etc.
1743 setUndo(bview, Undo::DELETE,
1744 selection.start.par(), undoendpar);
1746 // there are two cases: cut only within one paragraph or
1747 // more than one paragraph
1748 if (selection.start.par() == selection.end.par()) {
1749 // only within one paragraph
1750 endpar = selection.end.par();
1751 int pos = selection.end.pos();
1752 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1753 selection.start.pos(), pos,
1754 bview->buffer()->params.textclass,
1756 selection.end.pos(pos);
1758 endpar = selection.end.par();
1759 int pos = selection.end.pos();
1760 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1761 selection.start.pos(), pos,
1762 bview->buffer()->params.textclass,
1765 selection.end.par(endpar);
1766 selection.end.pos(pos);
1767 cursor.pos(selection.end.pos());
1769 endpar = endpar->next();
1771 // sometimes necessary
1773 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1775 redoParagraphs(bview, selection.start, endpar);
1777 // cutSelection can invalidate the cursor so we need to set
1779 cursor = selection.start;
1781 // need a valid cursor. (Lgb)
1784 setCursor(bview, cursor.par(), cursor.pos());
1785 selection.cursor = cursor;
1786 updateCounters(bview, cursor.row());
1790 void LyXText::copySelection(BufferView * bview)
1792 // Stuff what we got on the clipboard. Even if there is no selection.
1794 // There is a problem with having the stuffing here in that the
1795 // larger the selection the slower LyX will get. This can be
1796 // solved by running the line below only when the selection has
1797 // finished. The solution used currently just works, to make it
1798 // faster we need to be more clever and probably also have more
1799 // calls to stuffClipboard. (Lgb)
1800 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1802 // this doesnt make sense, if there is no selection
1803 if (!selection.set())
1806 // ok we have a selection. This is always between selection.start
1807 // and sel_end cursor
1809 // copy behind a space if there is one
1810 while (selection.start.par()->size() > selection.start.pos()
1811 && selection.start.par()->isLineSeparator(selection.start.pos())
1812 && (selection.start.par() != selection.end.par()
1813 || selection.start.pos() < selection.end.pos()))
1814 selection.start.pos(selection.start.pos() + 1);
1816 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1817 selection.start.pos(), selection.end.pos(),
1818 bview->buffer()->params.textclass);
1822 void LyXText::pasteSelection(BufferView * bview)
1824 // this does not make sense, if there is nothing to paste
1825 if (!CutAndPaste::checkPastePossible(cursor.par()))
1828 setUndo(bview, Undo::INSERT,
1829 cursor.par(), cursor.par()->next());
1832 Paragraph * actpar = cursor.par();
1833 int pos = cursor.pos();
1835 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1836 bview->buffer()->params.textclass);
1838 redoParagraphs(bview, cursor, endpar);
1840 setCursor(bview, cursor.par(), cursor.pos());
1843 setCursor(bview, actpar, pos);
1844 updateCounters(bview, cursor.row());
1848 // returns a pointer to the very first Paragraph
1849 Paragraph * LyXText::firstParagraph() const
1851 return ownerParagraph();
1855 // sets the selection over the number of characters of string, no check!!
1856 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1861 selection.cursor = cursor;
1862 for (string::size_type i = 0; i < str.length(); ++i)
1864 setSelection(bview);
1868 // simple replacing. The font of the first selected character is used
1869 void LyXText::replaceSelectionWithString(BufferView * bview,
1872 setCursorParUndo(bview);
1875 if (!selection.set()) { // create a dummy selection
1876 selection.end = cursor;
1877 selection.start = cursor;
1880 // Get font setting before we cut
1881 Paragraph::size_type pos = selection.end.pos();
1882 LyXFont const font = selection.start.par()
1883 ->getFontSettings(bview->buffer()->params,
1884 selection.start.pos());
1886 // Insert the new string
1887 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1888 selection.end.par()->insertChar(pos, (*cit), font);
1892 // Cut the selection
1893 cutSelection(bview, true, false);
1899 // needed to insert the selection
1900 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1902 Paragraph * par = cursor.par();
1903 Paragraph::size_type pos = cursor.pos();
1904 Paragraph * endpar = cursor.par()->next();
1906 setCursorParUndo(bview);
1908 // only to be sure, should not be neccessary
1911 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1913 redoParagraphs(bview, cursor, endpar);
1914 setCursor(bview, cursor.par(), cursor.pos());
1915 selection.cursor = cursor;
1916 setCursor(bview, par, pos);
1917 setSelection(bview);
1921 // turns double-CR to single CR, others where converted into one
1922 // blank. Then InsertStringAsLines is called
1923 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1925 string linestr(str);
1926 bool newline_inserted = false;
1927 for (string::size_type i = 0; i < linestr.length(); ++i) {
1928 if (linestr[i] == '\n') {
1929 if (newline_inserted) {
1930 // we know that \r will be ignored by
1931 // InsertStringA. Of course, it is a dirty
1932 // trick, but it works...
1933 linestr[i - 1] = '\r';
1937 newline_inserted = true;
1939 } else if (IsPrintable(linestr[i])) {
1940 newline_inserted = false;
1943 insertStringAsLines(bview, linestr);
1947 bool LyXText::gotoNextInset(BufferView * bview,
1948 std::vector<Inset::Code> const & codes,
1949 string const & contents) const
1951 LyXCursor res = cursor;
1954 if (res.pos() < res.par()->size() - 1) {
1955 res.pos(res.pos() + 1);
1957 res.par(res.par()->next());
1961 } while (res.par() &&
1962 !(res.par()->isInset(res.pos())
1963 && (inset = res.par()->getInset(res.pos())) != 0
1964 && find(codes.begin(), codes.end(), inset->lyxCode())
1966 && (contents.empty() ||
1967 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1971 setCursor(bview, res.par(), res.pos(), false);
1978 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1979 Paragraph::size_type pos)
1981 LyXCursor tmpcursor;
1984 Paragraph::size_type z;
1985 Row * row = getRow(par, pos, y);
1987 // is there a break one row above
1988 if (row->previous() && row->previous()->par() == row->par()) {
1989 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1990 if (z >= row->pos()) {
1991 // set the dimensions of the row above
1992 y -= row->previous()->height();
1994 refresh_row = row->previous();
1995 status(bview, LyXText::NEED_MORE_REFRESH);
1997 breakAgain(bview, row->previous());
1999 // set the cursor again. Otherwise
2000 // dangling pointers are possible
2001 setCursor(bview, cursor.par(), cursor.pos(),
2002 false, cursor.boundary());
2003 selection.cursor = cursor;
2008 int const tmpheight = row->height();
2009 Paragraph::size_type const tmplast = rowLast(row);
2013 breakAgain(bview, row);
2014 if (row->height() == tmpheight && rowLast(row) == tmplast)
2015 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2017 status(bview, LyXText::NEED_MORE_REFRESH);
2019 // check the special right address boxes
2020 if (textclasslist.Style(bview->buffer()->params.textclass,
2021 par->getLayout()).margintype
2022 == MARGIN_RIGHT_ADDRESS_BOX)
2030 redoDrawingOfParagraph(bview, tmpcursor);
2033 // set the cursor again. Otherwise dangling pointers are possible
2034 // also set the selection
2036 if (selection.set()) {
2038 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2039 false, selection.cursor.boundary());
2040 selection.cursor = cursor;
2041 setCursorIntern(bview, selection.start.par(),
2042 selection.start.pos(),
2043 false, selection.start.boundary());
2044 selection.start = cursor;
2045 setCursorIntern(bview, selection.end.par(),
2046 selection.end.pos(),
2047 false, selection.end.boundary());
2048 selection.end = cursor;
2049 setCursorIntern(bview, last_sel_cursor.par(),
2050 last_sel_cursor.pos(),
2051 false, last_sel_cursor.boundary());
2052 last_sel_cursor = cursor;
2055 setCursorIntern(bview, cursor.par(), cursor.pos(),
2056 false, cursor.boundary());
2060 // returns false if inset wasn't found
2061 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2063 // first check the current paragraph
2064 int pos = cursor.par()->getPositionOfInset(inset);
2066 checkParagraph(bview, cursor.par(), pos);
2070 // check every paragraph
2072 Paragraph * par = firstParagraph();
2074 pos = par->getPositionOfInset(inset);
2076 checkParagraph(bview, par, pos);
2086 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2087 Paragraph::size_type pos,
2088 bool setfont, bool boundary) const
2090 LyXCursor old_cursor = cursor;
2091 setCursorIntern(bview, par, pos, setfont, boundary);
2092 deleteEmptyParagraphMechanism(bview, old_cursor);
2096 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2097 Paragraph::size_type pos, bool boundary) const
2101 cur.boundary(boundary);
2103 // get the cursor y position in text
2105 Row * row = getRow(par, pos, y);
2106 // y is now the beginning of the cursor row
2107 y += row->baseline();
2108 // y is now the cursor baseline
2111 // now get the cursors x position
2113 float fill_separator;
2115 float fill_label_hfill;
2116 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2118 Paragraph::size_type cursor_vpos = 0;
2119 Paragraph::size_type last = rowLastPrintable(row);
2121 if (pos > last + 1) // This shouldn't happen.
2123 else if (pos < row->pos())
2126 if (last < row->pos())
2127 cursor_vpos = row->pos();
2128 else if (pos > last && !boundary)
2129 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2130 ? row->pos() : last + 1;
2131 else if (pos > row->pos() &&
2132 (pos > last || boundary))
2133 /// Place cursor after char at (logical) position pos - 1
2134 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2135 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2137 /// Place cursor before char at (logical) position pos
2138 cursor_vpos = (bidi_level(pos) % 2 == 0)
2139 ? log2vis(pos) : log2vis(pos) + 1;
2141 Paragraph::size_type main_body =
2142 beginningOfMainBody(bview->buffer(), row->par());
2143 if ((main_body > 0) &&
2144 ((main_body-1 > last) ||
2145 !row->par()->isLineSeparator(main_body-1)))
2148 for (Paragraph::size_type vpos = row->pos();
2149 vpos < cursor_vpos; ++vpos) {
2150 pos = vis2log(vpos);
2151 if (main_body > 0 && pos == main_body - 1) {
2152 x += fill_label_hfill +
2153 lyxfont::width(textclasslist.Style(
2154 bview->buffer()->params.textclass,
2155 row->par()->getLayout())
2157 getLabelFont(bview->buffer(), row->par()));
2158 if (row->par()->isLineSeparator(main_body-1))
2159 x -= singleWidth(bview, row->par(),main_body-1);
2161 if (hfillExpansion(bview->buffer(), row, pos)) {
2162 x += singleWidth(bview, row->par(), pos);
2163 if (pos >= main_body)
2166 x += fill_label_hfill;
2167 } else if (row->par()->isSeparator(pos)) {
2168 x += singleWidth(bview, row->par(), pos);
2169 if (pos >= main_body)
2170 x += fill_separator;
2172 x += singleWidth(bview, row->par(), pos);
2181 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2182 Paragraph::size_type pos,
2183 bool setfont, bool boundary) const
2185 InsetText * it = static_cast<InsetText *>(par->inInset());
2187 if (it != inset_owner) {
2188 lyxerr << "InsetText is " << it << endl;
2189 lyxerr << "inset_owner is " << inset_owner << endl;
2190 #warning I believe this code is wrong. (Lgb)
2191 #warning Jürgen, have a look at this. (Lgb)
2192 #warning Hmmm, I guess you are right but we
2193 #warning should verify when this is needed
2194 // Jürgen, would you like to have a look?
2195 // I guess we need to move the outer cursor
2196 // and open and lock the inset (bla bla bla)
2197 // stuff I don't know... so can you have a look?
2199 // I moved the lyxerr stuff in here so we can see if
2200 // this is actually really needed and where!
2202 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2208 setCursor(bview, cursor, par, pos, boundary);
2210 setCurrentFont(bview);
2214 void LyXText::setCurrentFont(BufferView * bview) const
2216 Paragraph::size_type pos = cursor.pos();
2217 if (cursor.boundary() && pos > 0)
2221 if (pos == cursor.par()->size())
2223 else // potentional bug... BUG (Lgb)
2224 if (cursor.par()->isSeparator(pos)) {
2225 if (pos > cursor.row()->pos() &&
2226 bidi_level(pos) % 2 ==
2227 bidi_level(pos - 1) % 2)
2229 else if (pos + 1 < cursor.par()->size())
2235 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2236 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2238 if (cursor.pos() == cursor.par()->size() &&
2239 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2240 !cursor.boundary()) {
2241 Language const * lang =
2242 cursor.par()->getParLanguage(bview->buffer()->params);
2243 current_font.setLanguage(lang);
2244 current_font.setNumber(LyXFont::OFF);
2245 real_current_font.setLanguage(lang);
2246 real_current_font.setNumber(LyXFont::OFF);
2251 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2253 LyXCursor old_cursor = cursor;
2255 setCursorFromCoordinates(bview, cursor, x, y);
2256 setCurrentFont(bview);
2257 deleteEmptyParagraphMechanism(bview, old_cursor);
2261 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2264 // Get the row first.
2266 Row * row = getRowNearY(y);
2268 Paragraph::size_type const column = getColumnNearX(bview, row, x,
2271 cur.par(row->par());
2272 cur.pos(row->pos() + column);
2274 cur.y(y + row->baseline());
2276 cur.boundary(bound);
2280 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2282 if (cursor.pos() > 0) {
2283 bool boundary = cursor.boundary();
2284 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2285 if (!internal && !boundary &&
2286 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2287 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2288 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2289 Paragraph * par = cursor.par()->previous();
2290 setCursor(bview, par, par->size());
2295 void LyXText::cursorRight(BufferView * bview, bool internal) const
2297 if (!internal && cursor.boundary() &&
2298 !cursor.par()->isNewline(cursor.pos()))
2299 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2300 else if (cursor.pos() < cursor.par()->size()) {
2301 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2303 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2304 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2305 } else if (cursor.par()->next())
2306 setCursor(bview, cursor.par()->next(), 0);
2310 void LyXText::cursorUp(BufferView * bview) const
2312 setCursorFromCoordinates(bview, cursor.x_fix(),
2313 cursor.y() - cursor.row()->baseline() - 1);
2317 void LyXText::cursorDown(BufferView * bview) const
2319 setCursorFromCoordinates(bview, cursor.x_fix(),
2320 cursor.y() - cursor.row()->baseline()
2321 + cursor.row()->height() + 1);
2325 void LyXText::cursorUpParagraph(BufferView * bview) const
2327 if (cursor.pos() > 0) {
2328 setCursor(bview, cursor.par(), 0);
2330 else if (cursor.par()->previous()) {
2331 setCursor(bview, cursor.par()->previous(), 0);
2336 void LyXText::cursorDownParagraph(BufferView * bview) const
2338 if (cursor.par()->next()) {
2339 setCursor(bview, cursor.par()->next(), 0);
2341 setCursor(bview, cursor.par(), cursor.par()->size());
2345 // fix the cursor `cur' after a characters has been deleted at `where'
2346 // position. Called by deleteEmptyParagraphMechanism
2347 void LyXText::fixCursorAfterDelete(BufferView * bview,
2349 LyXCursor const & where) const
2351 // if cursor is not in the paragraph where the delete occured,
2353 if (cur.par() != where.par())
2356 // if cursor position is after the place where the delete occured,
2358 if (cur.pos() > where.pos())
2359 cur.pos(cur.pos()-1);
2361 // recompute row et al. for this cursor
2362 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2366 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2367 LyXCursor const & old_cursor) const
2369 // Would be wrong to delete anything if we have a selection.
2370 if (selection.set()) return;
2372 // We allow all kinds of "mumbo-jumbo" when freespacing.
2373 if (textclasslist.Style(bview->buffer()->params.textclass,
2374 old_cursor.par()->getLayout()).free_spacing)
2377 bool deleted = false;
2379 /* Ok I'll put some comments here about what is missing.
2380 I have fixed BackSpace (and thus Delete) to not delete
2381 double-spaces automagically. I have also changed Cut,
2382 Copy and Paste to hopefully do some sensible things.
2383 There are still some small problems that can lead to
2384 double spaces stored in the document file or space at
2385 the beginning of paragraphs. This happens if you have
2386 the cursor betwenn to spaces and then save. Or if you
2387 cut and paste and the selection have a space at the
2388 beginning and then save right after the paste. I am
2389 sure none of these are very hard to fix, but I will
2390 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2391 that I can get some feedback. (Lgb)
2394 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2395 // delete the LineSeparator.
2398 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2399 // delete the LineSeparator.
2402 // If the pos around the old_cursor were spaces, delete one of them.
2403 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2404 // Only if the cursor has really moved
2406 if (old_cursor.pos() > 0
2407 && old_cursor.pos() < old_cursor.par()->size()
2408 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2409 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2410 old_cursor.par()->erase(old_cursor.pos() - 1);
2411 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2413 #ifdef WITH_WARNINGS
2414 #warning This will not work anymore when we have multiple views of the same buffer
2415 // In this case, we will have to correct also the cursors held by
2416 // other bufferviews. It will probably be easier to do that in a more
2417 // automated way in LyXCursor code. (JMarc 26/09/2001)
2419 // correct all cursors held by the LyXText
2420 fixCursorAfterDelete(bview, cursor, old_cursor);
2421 fixCursorAfterDelete(bview, selection.cursor,
2423 fixCursorAfterDelete(bview, selection.start,
2425 fixCursorAfterDelete(bview, selection.end, old_cursor);
2426 fixCursorAfterDelete(bview, last_sel_cursor,
2428 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2429 fixCursorAfterDelete(bview, toggle_end_cursor,
2435 // Do not delete empty paragraphs with keepempty set.
2436 if ((textclasslist.Style(bview->buffer()->params.textclass,
2437 old_cursor.par()->getLayout())).keepempty)
2440 // only do our magic if we changed paragraph
2441 if (old_cursor.par() == cursor.par())
2444 if ((old_cursor.par()->size() == 0
2445 || (old_cursor.par()->size() == 1
2446 && old_cursor.par()->isLineSeparator(0)))) {
2447 // ok, we will delete anything
2448 LyXCursor tmpcursor;
2450 // make sure that you do not delete any environments
2451 status(bview, LyXText::NEED_MORE_REFRESH);
2454 if (old_cursor.row()->previous()) {
2455 refresh_row = old_cursor.row()->previous();
2456 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2458 cursor = old_cursor; // that undo can restore the right cursor position
2459 Paragraph * endpar = old_cursor.par()->next();
2460 if (endpar && endpar->getDepth()) {
2461 while (endpar && endpar->getDepth()) {
2462 endpar = endpar->next();
2465 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2469 removeRow(old_cursor.row());
2470 if (ownerParagraph() == old_cursor.par()) {
2471 ownerParagraph(ownerParagraph()->next());
2474 delete old_cursor.par();
2476 /* Breakagain the next par. Needed because of
2477 * the parindent that can occur or dissappear.
2478 * The next row can change its height, if
2479 * there is another layout before */
2480 if (refresh_row->next()) {
2481 breakAgain(bview, refresh_row->next());
2482 updateCounters(bview, refresh_row);
2484 setHeightOfRow(bview, refresh_row);
2486 refresh_row = old_cursor.row()->next();
2487 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2490 cursor = old_cursor; // that undo can restore the right cursor position
2491 Paragraph * endpar = old_cursor.par()->next();
2492 if (endpar && endpar->getDepth()) {
2493 while (endpar && endpar->getDepth()) {
2494 endpar = endpar->next();
2497 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2501 removeRow(old_cursor.row());
2503 if (ownerParagraph() == old_cursor.par()) {
2504 ownerParagraph(ownerParagraph()->next());
2507 delete old_cursor.par();
2509 /* Breakagain the next par. Needed because of
2510 the parindent that can occur or dissappear.
2511 The next row can change its height, if
2512 there is another layout before */
2514 breakAgain(bview, refresh_row);
2515 updateCounters(bview, refresh_row->previous());
2520 setCursorIntern(bview, cursor.par(), cursor.pos());
2522 if (selection.cursor.par() == old_cursor.par()
2523 && selection.cursor.pos() == selection.cursor.pos()) {
2524 // correct selection
2525 selection.cursor = cursor;
2529 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2530 redoParagraphs(bview, old_cursor,
2531 old_cursor.par()->next());
2533 setCursorIntern(bview, cursor.par(), cursor.pos());
2534 selection.cursor = cursor;
2540 void LyXText::toggleAppendix(BufferView * bview)
2542 Paragraph * par = cursor.par();
2543 bool start = !par->params().startOfAppendix();
2545 // ensure that we have only one start_of_appendix in this document
2546 Paragraph * tmp = firstParagraph();
2547 for (; tmp; tmp = tmp->next()) {
2548 tmp->params().startOfAppendix(false);
2551 par->params().startOfAppendix(start);
2553 // we can set the refreshing parameters now
2554 status(bview, LyXText::NEED_MORE_REFRESH);
2556 refresh_row = 0; // not needed for full update
2557 updateCounters(bview, 0);
2558 setCursor(bview, cursor.par(), cursor.pos());
2562 Paragraph * LyXText::ownerParagraph() const
2565 return inset_owner->paragraph();
2567 return bv_owner->buffer()->paragraph;
2571 void LyXText::ownerParagraph(Paragraph * p) const
2574 inset_owner->paragraph(p);
2576 bv_owner->buffer()->paragraph = p;
2581 void LyXText::ownerParagraph(int id, Paragraph * p) const
2583 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2584 if (op && op->inInset()) {
2585 static_cast<InsetText *>(op->inInset())->paragraph(p);
2588 inset_owner->paragraph(p);
2590 bv_owner->buffer()->paragraph = p;
2596 LyXText::text_status LyXText::status() const
2602 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2604 // well as much as I know && binds more then || so the above and the
2605 // below are identical (this for your known use of parentesis!)
2606 // Now some explanation:
2607 // We should only go up with refreshing code so this means that if
2608 // we have a MORE refresh we should never set it to LITTLE if we still
2609 // didn't handle it (and then it will be UNCHANGED. Now as long as
2610 // we stay inside one LyXText this may work but we need to tell the
2611 // outermost LyXText that it should REALLY draw us if there is some
2612 // change in a Inset::LyXText. So you see that when we are inside a
2613 // inset's LyXText we give the LITTLE to the outermost LyXText to
2614 // tell'em that it should redraw the actual row (where the inset
2615 // resides! Capito?!
2617 if ((status_ != NEED_MORE_REFRESH)
2618 || (status_ == NEED_MORE_REFRESH
2619 && st != NEED_VERY_LITTLE_REFRESH))
2622 if (inset_owner && st != UNCHANGED) {
2623 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2624 if (!bview->text->refresh_row) {
2625 bview->text->refresh_row = bview->text->cursor.row();
2626 bview->text->refresh_y = bview->text->cursor.y() -
2627 bview->text->cursor.row()->baseline();