1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
27 #include "support/textutils.h"
28 #include "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
41 #include "FloatList.h"
43 #include "ParagraphParameters.h"
44 #include "support/LAssert.h"
53 LyXText::LyXText(BufferView * bv)
54 : number_of_rows(0), height(0), width(0), first(0),
55 bv_owner(bv), inset_owner(0), the_locking_inset(0),
56 need_break_row(0), refresh_y(0), refresh_row(0),
57 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
62 LyXText::LyXText(InsetText * inset)
63 : number_of_rows(0), height(0), width(0), first(0),
64 bv_owner(0), inset_owner(inset), the_locking_inset(0),
65 need_break_row(0), refresh_y(0), refresh_row(0),
66 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
70 void LyXText::init(BufferView * bview, bool reinit)
73 // Delete all rows, this does not touch the paragraphs!
74 Row * tmprow = firstrow;
76 tmprow = firstrow->next();
80 lastrow = refresh_row = need_break_row = 0;
81 width = height = copylayouttype = 0;
82 number_of_rows = first = refresh_y = 0;
83 status_ = LyXText::UNCHANGED;
87 Paragraph * par = ownerParagraph();
88 current_font = getFont(bview->buffer(), par, 0);
90 insertParagraph(bview, par, lastrow);
93 setCursorIntern(bview, firstrow->par(), 0);
94 selection.cursor = cursor;
100 // Delete all rows, this does not touch the paragraphs!
101 Row * tmprow = firstrow;
103 tmprow = firstrow->next();
112 LyXFont const realizeFont(LyXFont const & font,
116 LyXFont tmpfont(font);
117 Paragraph::depth_type par_depth = par->getDepth();
119 // Resolve against environment font information
120 while (par && par_depth && !tmpfont.resolved()) {
121 par = par->outerHook();
123 #ifndef INHERIT_LANGUAGE
124 tmpfont.realize(textclasslist.
125 Style(buf->params.textclass,
126 par->getLayout()).font);
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font,
131 buf->params.language);
133 par_depth = par->getDepth();
137 #ifndef INHERIT_LANGUAGE
138 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
140 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
141 buf->params.language);
150 // Gets the fully instantiated font at a given position in a paragraph
151 // Basically the same routine as Paragraph::getFont() in paragraph.C.
152 // The difference is that this one is used for displaying, and thus we
153 // are allowed to make cosmetic improvements. For instance make footnotes
155 // If position is -1, we get the layout font of the paragraph.
156 // If position is -2, we get the font of the manual label of the paragraph.
157 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
158 Paragraph::size_type pos) const
160 lyx::Assert(pos >= 0);
162 LyXLayout const & layout =
163 textclasslist.Style(buf->params.textclass, par->getLayout());
165 Paragraph::depth_type par_depth = par->getDepth();
166 // We specialize the 95% common case:
168 if (layout.labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params,
173 #ifndef INHERIT_LANGUAGE
174 return f.realize(layout.reslabelfont);
176 return f.realize(layout.reslabelfont, buf->params.language);
179 LyXFont f = par->getFontSettings(buf->params, pos);
180 #ifndef INHERIT_LANGUAGE
181 return f.realize(layout.resfont);
183 return f.realize(layout.resfont, buf->params.language);
188 // The uncommon case need not be optimized as much
192 if (pos < beginningOfMainBody(buf, par)) {
194 layoutfont = layout.labelfont;
197 layoutfont = layout.font;
200 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
201 #ifndef INHERIT_LANGUAGE
202 tmpfont.realize(layoutfont);
204 tmpfont.realize(layoutfont, buf->params.language);
207 return realizeFont(tmpfont, buf, par);
211 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
213 LyXLayout const & layout =
214 textclasslist.Style(buf->params.textclass, par->getLayout());
216 Paragraph::depth_type par_depth = par->getDepth();
219 return layout.resfont;
222 return realizeFont(layout.font, buf, par);
226 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
228 LyXLayout const & layout =
229 textclasslist.Style(buf->params.textclass, par->getLayout());
231 Paragraph::depth_type par_depth = par->getDepth();
234 return layout.reslabelfont;
237 return realizeFont(layout.labelfont, buf, par);
241 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
242 Paragraph::size_type pos, LyXFont const & fnt,
245 Buffer const * buf = bv->buffer();
246 LyXFont font = getFont(buf, par, pos);
247 font.update(fnt, buf->params.language, toggleall);
248 // Let the insets convert their font
249 if (par->getChar(pos) == Paragraph::META_INSET) {
250 Inset * inset = par->getInset(pos);
252 if (inset->editable()==Inset::IS_EDITABLE) {
253 UpdatableInset * uinset =
254 static_cast<UpdatableInset *>(inset);
255 uinset->setFont(bv, fnt, toggleall, true);
260 LyXLayout const & layout =
261 textclasslist.Style(buf->params.textclass,
264 // Get concrete layout font to reduce against
267 if (pos < beginningOfMainBody(buf, par))
268 layoutfont = layout.labelfont;
270 layoutfont = layout.font;
272 // Realize against environment font information
273 if (par->getDepth()){
274 Paragraph * tp = par;
275 while (!layoutfont.resolved() && tp && tp->getDepth()) {
276 tp = tp->outerHook();
278 #ifndef INHERIT_LANGUAGE
279 layoutfont.realize(textclasslist.
280 Style(buf->params.textclass,
281 tp->getLayout()).font);
283 layoutfont.realize(textclasslist.
284 Style(buf->params.textclass,
285 tp->getLayout()).font,
286 buf->params.language);
291 #ifndef INHERIT_LANGUAGE
292 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
295 buf->params.language);
298 // Now, reduce font against full layout font
299 font.reduce(layoutfont);
301 par->setFont(pos, font);
305 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
306 Paragraph::size_type pos, LyXFont const & fnt)
310 LyXLayout const & layout =
311 textclasslist.Style(buf->params.textclass,
314 // Get concrete layout font to reduce against
317 if (pos < beginningOfMainBody(buf, par))
318 layoutfont = layout.labelfont;
320 layoutfont = layout.font;
322 // Realize against environment font information
323 if (par->getDepth()){
324 Paragraph * tp = par;
325 while (!layoutfont.resolved() && tp && tp->getDepth()) {
326 tp = tp->outerHook();
328 #ifndef INHERIT_LANGUAGE
329 layoutfont.realize(textclasslist.
330 Style(buf->params.textclass,
331 tp->getLayout()).font);
333 layoutfont.realize(textclasslist.
334 Style(buf->params.textclass,
335 tp->getLayout()).font,
336 buf->params.language);
341 #ifndef INHERIT_LANGUAGE
342 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
345 buf->params.language);
348 // Now, reduce font against full layout font
349 font.reduce(layoutfont);
351 par->setFont(pos, font);
355 // inserts a new row behind the specified row, increments
356 // the touched counters
357 void LyXText::insertRow(Row * row, Paragraph * par,
358 Paragraph::size_type pos) const
360 Row * tmprow = new Row;
363 tmprow->next(firstrow);
366 tmprow->previous(row);
367 tmprow->next(row->next());
372 tmprow->next()->previous(tmprow);
374 if (tmprow->previous())
375 tmprow->previous()->next(tmprow);
387 // removes the row and reset the touched counters
388 void LyXText::removeRow(Row * row) const
390 /* this must not happen before the currentrow for clear reasons.
391 so the trick is just to set the current row onto the previous
394 getRow(row->par(), row->pos(), unused_y);
397 row->next()->previous(row->previous());
398 if (!row->previous()) {
399 firstrow = row->next();
401 row->previous()->next(row->next());
404 lastrow = row->previous();
406 height -= row->height(); // the text becomes smaller
409 --number_of_rows; // one row less
413 // remove all following rows of the paragraph of the specified row.
414 void LyXText::removeParagraph(Row * row) const
416 Paragraph * tmppar = row->par();
420 while (row && row->par() == tmppar) {
421 tmprow = row->next();
428 // insert the specified paragraph behind the specified row
429 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
432 insertRow(row, par, 0); /* insert a new row, starting
435 setCounter(bview->buffer(), par); // set the counters
437 // and now append the whole paragraph behind the new row
440 appendParagraph(bview, firstrow);
442 row->next()->height(0);
443 appendParagraph(bview, row->next());
448 Inset * LyXText::getInset() const
451 if (cursor.pos() == 0 && cursor.par()->bibkey) {
452 inset = cursor.par()->bibkey;
453 } else if (cursor.pos() < cursor.par()->size()
454 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
455 inset = cursor.par()->getInset(cursor.pos());
461 void LyXText::toggleInset(BufferView * bview)
463 Inset * inset = getInset();
464 if (!inset->editable())
466 //bview->owner()->message(inset->editMessage());
468 // do we want to keep this?? (JMarc)
469 if (inset->editable() != Inset::HIGHLY_EDITABLE)
470 setCursorParUndo(bview);
472 if (inset->isOpen()) {
478 inset->open(bview, !inset->isOpen());
483 /* used in setlayout */
484 // Asger is not sure we want to do this...
485 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
488 LyXLayout const & layout =
489 textclasslist.Style(buf->params.textclass, par->getLayout());
492 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
493 if (pos < beginningOfMainBody(buf, par))
494 layoutfont = layout.labelfont;
496 layoutfont = layout.font;
498 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
499 tmpfont.reduce(layoutfont);
500 par->setFont(pos, tmpfont);
505 Paragraph * LyXText::setLayout(BufferView * bview,
506 LyXCursor & cur, LyXCursor & sstart_cur,
507 LyXCursor & send_cur,
508 LyXTextClass::size_type layout)
510 Paragraph * endpar = send_cur.par()->next();
511 Paragraph * undoendpar = endpar;
513 if (endpar && endpar->getDepth()) {
514 while (endpar && endpar->getDepth()) {
515 endpar = endpar->next();
519 endpar = endpar->next(); // because of parindents etc.
522 setUndo(bview, Undo::EDIT,
523 sstart_cur.par(), undoendpar);
525 // ok we have a selection. This is always between sstart_cur
526 // and sel_end cursor
529 LyXLayout const & lyxlayout =
530 textclasslist.Style(bview->buffer()->params.textclass, layout);
532 while (cur.par() != send_cur.par()) {
533 cur.par()->setLayout(layout);
534 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
535 Paragraph * fppar = cur.par();
536 fppar->params().spaceTop(lyxlayout.fill_top ?
537 VSpace(VSpace::VFILL)
538 : VSpace(VSpace::NONE));
539 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
540 VSpace(VSpace::VFILL)
541 : VSpace(VSpace::NONE));
542 if (lyxlayout.margintype == MARGIN_MANUAL)
543 cur.par()->setLabelWidthString(lyxlayout.labelstring());
544 if (lyxlayout.labeltype != LABEL_BIBLIO
546 delete fppar->bibkey;
549 cur.par(cur.par()->next());
551 cur.par()->setLayout(layout);
552 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
553 Paragraph * fppar = cur.par();
554 fppar->params().spaceTop(lyxlayout.fill_top ?
555 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
556 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
557 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
558 if (lyxlayout.margintype == MARGIN_MANUAL)
559 cur.par()->setLabelWidthString(lyxlayout.labelstring());
560 if (lyxlayout.labeltype != LABEL_BIBLIO
562 delete fppar->bibkey;
569 // set layout over selection and make a total rebreak of those paragraphs
570 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
572 LyXCursor tmpcursor = cursor; /* store the current cursor */
574 // if there is no selection just set the layout
575 // of the current paragraph */
576 if (!selection.set()) {
577 selection.start = cursor; // dummy selection
578 selection.end = cursor;
580 Paragraph * endpar = setLayout(bview, cursor, selection.start,
581 selection.end, layout);
582 redoParagraphs(bview, selection.start, endpar);
584 // we have to reset the selection, because the
585 // geometry could have changed
586 setCursor(bview, selection.start.par(),
587 selection.start.pos(), false);
588 selection.cursor = cursor;
589 setCursor(bview, selection.end.par(), selection.end.pos(), false);
590 updateCounters(bview, cursor.row());
593 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
597 // increment depth over selection and
598 // make a total rebreak of those paragraphs
599 void LyXText::incDepth(BufferView * bview)
601 // If there is no selection, just use the current paragraph
602 if (!selection.set()) {
603 selection.start = cursor; // dummy selection
604 selection.end = cursor;
607 // We end at the next paragraph with depth 0
608 Paragraph * endpar = selection.end.par()->next();
610 Paragraph * undoendpar = endpar;
612 if (endpar && endpar->getDepth()) {
613 while (endpar && endpar->getDepth()) {
614 endpar = endpar->next();
618 endpar = endpar->next(); // because of parindents etc.
621 setUndo(bview, Undo::EDIT,
622 selection.start.par(), undoendpar);
624 LyXCursor tmpcursor = cursor; // store the current cursor
626 // ok we have a selection. This is always between sel_start_cursor
627 // and sel_end cursor
628 cursor = selection.start;
630 bool anything_changed = false;
633 // NOTE: you can't change the depth of a bibliography entry
635 textclasslist.Style(bview->buffer()->params.textclass,
636 cursor.par()->getLayout()
637 ).labeltype != LABEL_BIBLIO) {
638 Paragraph * prev = cursor.par()->previous();
641 && (prev->getDepth() - cursor.par()->getDepth() > 0
642 || (prev->getDepth() == cursor.par()->getDepth()
643 && textclasslist.Style(bview->buffer()->params.textclass,
644 prev->getLayout()).isEnvironment()))) {
645 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
646 anything_changed = true;
649 if (cursor.par() == selection.end.par())
651 cursor.par(cursor.par()->next());
654 // if nothing changed set all depth to 0
655 if (!anything_changed) {
656 cursor = selection.start;
657 while (cursor.par() != selection.end.par()) {
658 cursor.par()->params().depth(0);
659 cursor.par(cursor.par()->next());
661 cursor.par()->params().depth(0);
664 redoParagraphs(bview, selection.start, endpar);
666 // we have to reset the selection, because the
667 // geometry could have changed
668 setCursor(bview, selection.start.par(), selection.start.pos());
669 selection.cursor = cursor;
670 setCursor(bview, selection.end.par(), selection.end.pos());
671 updateCounters(bview, cursor.row());
674 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
678 // decrement depth over selection and
679 // make a total rebreak of those paragraphs
680 void LyXText::decDepth(BufferView * bview)
682 // if there is no selection just set the layout
683 // of the current paragraph
684 if (!selection.set()) {
685 selection.start = cursor; // dummy selection
686 selection.end = cursor;
688 Paragraph * endpar = selection.end.par()->next();
689 Paragraph * undoendpar = endpar;
691 if (endpar && endpar->getDepth()) {
692 while (endpar && endpar->getDepth()) {
693 endpar = endpar->next();
697 endpar = endpar->next(); // because of parindents etc.
700 setUndo(bview, Undo::EDIT,
701 selection.start.par(), undoendpar);
703 LyXCursor tmpcursor = cursor; // store the current cursor
705 // ok we have a selection. This is always between sel_start_cursor
706 // and sel_end cursor
707 cursor = selection.start;
710 if (cursor.par()->params().depth()) {
711 cursor.par()->params()
712 .depth(cursor.par()->params().depth() - 1);
714 if (cursor.par() == selection.end.par()) {
717 cursor.par(cursor.par()->next());
720 redoParagraphs(bview, selection.start, endpar);
722 // we have to reset the selection, because the
723 // geometry could have changed
724 setCursor(bview, selection.start.par(),
725 selection.start.pos());
726 selection.cursor = cursor;
727 setCursor(bview, selection.end.par(), selection.end.pos());
728 updateCounters(bview, cursor.row());
731 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
735 // set font over selection and make a total rebreak of those paragraphs
736 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
738 // if there is no selection just set the current_font
739 if (!selection.set()) {
740 // Determine basis font
742 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
744 layoutfont = getLabelFont(bview->buffer(),
747 layoutfont = getLayoutFont(bview->buffer(),
750 // Update current font
751 real_current_font.update(font,
752 bview->buffer()->params.language,
755 // Reduce to implicit settings
756 current_font = real_current_font;
757 current_font.reduce(layoutfont);
758 // And resolve it completely
759 #ifndef INHERIT_LANGUAGE
760 real_current_font.realize(layoutfont);
762 real_current_font.realize(layoutfont,
763 bview->buffer()->params.language);
768 LyXCursor tmpcursor = cursor; // store the current cursor
770 // ok we have a selection. This is always between sel_start_cursor
771 // and sel_end cursor
773 setUndo(bview, Undo::EDIT,
774 selection.start.par(), selection.end.par()->next());
776 cursor = selection.start;
777 while (cursor.par() != selection.end.par() ||
778 (cursor.pos() < selection.end.pos()))
780 if (cursor.pos() < cursor.par()->size()) {
781 // an open footnote should behave
783 setCharFont(bview, cursor.par(), cursor.pos(),
785 cursor.pos(cursor.pos() + 1);
788 cursor.par(cursor.par()->next());
793 redoParagraphs(bview, selection.start, selection.end.par()->next());
795 // we have to reset the selection, because the
796 // geometry could have changed
797 setCursor(bview, selection.start.par(), selection.start.pos());
798 selection.cursor = cursor;
799 setCursor(bview, selection.end.par(), selection.end.pos());
802 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
803 tmpcursor.boundary());
807 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
809 Row * tmprow = cur.row();
810 int y = cur.y() - tmprow->baseline();
812 setHeightOfRow(bview, tmprow);
814 while (tmprow->previous()
815 && tmprow->previous()->par() == tmprow->par()) {
816 tmprow = tmprow->previous();
817 y -= tmprow->height();
818 setHeightOfRow(bview, tmprow);
821 // we can set the refreshing parameters now
822 status(bview, LyXText::NEED_MORE_REFRESH);
824 refresh_row = tmprow;
825 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
829 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
831 Row * tmprow = cur.row();
833 int y = cur.y() - tmprow->baseline();
834 setHeightOfRow(bview, tmprow);
836 while (tmprow->previous()
837 && tmprow->previous()->par() == tmprow->par()) {
838 tmprow = tmprow->previous();
839 y -= tmprow->height();
842 // we can set the refreshing parameters now
843 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
845 refresh_row = tmprow;
847 status(bview, LyXText::NEED_MORE_REFRESH);
848 setCursor(bview, cur.par(), cur.pos());
852 // deletes and inserts again all paragaphs between the cursor
853 // and the specified par
854 // This function is needed after SetLayout and SetFont etc.
855 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
856 Paragraph const * endpar) const
859 Paragraph * tmppar = 0;
860 Paragraph * first_phys_par = 0;
862 Row * tmprow = cur.row();
864 int y = cur.y() - tmprow->baseline();
866 if (!tmprow->previous()) {
867 // a trick/hack for UNDO
868 // Can somebody please tell me _why_ this solves
870 first_phys_par = firstParagraph();
872 first_phys_par = tmprow->par();
873 while (tmprow->previous()
874 && tmprow->previous()->par() == first_phys_par)
876 tmprow = tmprow->previous();
877 y -= tmprow->height();
881 // we can set the refreshing parameters now
882 status(bview, LyXText::NEED_MORE_REFRESH);
884 refresh_row = tmprow->previous(); /* the real refresh row will
885 be deleted, so I store
889 tmppar = tmprow->next()->par();
892 while (tmppar != endpar) {
893 removeRow(tmprow->next());
895 tmppar = tmprow->next()->par();
900 // remove the first one
901 tmprow2 = tmprow; /* this is because tmprow->previous()
903 tmprow = tmprow->previous();
906 tmppar = first_phys_par;
910 insertParagraph(bview, tmppar, tmprow);
914 while (tmprow->next()
915 && tmprow->next()->par() == tmppar) {
916 tmprow = tmprow->next();
918 tmppar = tmppar->next();
920 } while (tmppar && tmppar != endpar);
922 // this is because of layout changes
924 refresh_y -= refresh_row->height();
925 setHeightOfRow(bview, refresh_row);
927 refresh_row = firstrow;
929 setHeightOfRow(bview, refresh_row);
932 if (tmprow && tmprow->next())
933 setHeightOfRow(bview, tmprow->next());
937 bool LyXText::fullRebreak(BufferView * bview)
943 if (need_break_row) {
944 breakAgain(bview, need_break_row);
952 // important for the screen
955 /* the cursor set functions have a special mechanism. When they
956 * realize, that you left an empty paragraph, they will delete it.
957 * They also delete the corresponding row */
959 // need the selection cursor:
960 void LyXText::setSelection(BufferView * bview)
962 bool const lsel = selection.set();
964 if (!selection.set()) {
965 last_sel_cursor = selection.cursor;
966 selection.start = selection.cursor;
967 selection.end = selection.cursor;
972 // first the toggling area
973 if (cursor.y() < last_sel_cursor.y()
974 || (cursor.y() == last_sel_cursor.y()
975 && cursor.x() < last_sel_cursor.x())) {
976 toggle_end_cursor = last_sel_cursor;
977 toggle_cursor = cursor;
979 toggle_end_cursor = cursor;
980 toggle_cursor = last_sel_cursor;
983 last_sel_cursor = cursor;
985 // and now the whole selection
987 if (selection.cursor.par() == cursor.par())
988 if (selection.cursor.pos() < cursor.pos()) {
989 selection.end = cursor;
990 selection.start = selection.cursor;
992 selection.end = selection.cursor;
993 selection.start = cursor;
995 else if (selection.cursor.y() < cursor.y() ||
996 (selection.cursor.y() == cursor.y()
997 && selection.cursor.x() < cursor.x())) {
998 selection.end = cursor;
999 selection.start = selection.cursor;
1002 selection.end = selection.cursor;
1003 selection.start = cursor;
1006 // a selection with no contents is not a selection
1007 if (selection.start.par() == selection.end.par() &&
1008 selection.start.pos() == selection.end.pos())
1009 selection.set(false);
1011 if (inset_owner && (selection.set() || lsel))
1012 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1016 string const LyXText::selectionAsString(Buffer const * buffer) const
1018 if (!selection.set()) return string();
1021 // Special handling if the whole selection is within one paragraph
1022 if (selection.start.par() == selection.end.par()) {
1023 result += selection.start.par()->asString(buffer,
1024 selection.start.pos(),
1025 selection.end.pos());
1029 // The selection spans more than one paragraph
1031 // First paragraph in selection
1032 result += selection.start.par()->asString(buffer,
1033 selection.start.pos(),
1034 selection.start.par()->size())
1037 // The paragraphs in between (if any)
1038 LyXCursor tmpcur(selection.start);
1039 tmpcur.par(tmpcur.par()->next());
1040 while (tmpcur.par() != selection.end.par()) {
1041 result += tmpcur.par()->asString(buffer, 0,
1042 tmpcur.par()->size()) +"\n\n";
1043 tmpcur.par(tmpcur.par()->next());
1046 // Last paragraph in selection
1047 result += selection.end.par()->asString(buffer, 0,
1048 selection.end.pos());
1054 void LyXText::clearSelection() const
1056 selection.set(false);
1057 selection.mark(false);
1058 selection.end = selection.start = selection.cursor = cursor;
1062 void LyXText::cursorHome(BufferView * bview) const
1064 setCursor(bview, cursor.par(), cursor.row()->pos());
1068 void LyXText::cursorEnd(BufferView * bview) const
1070 if (!cursor.row()->next()
1071 || cursor.row()->next()->par() != cursor.row()->par()) {
1072 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1074 if (cursor.par()->size() &&
1075 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1076 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1077 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1079 setCursor(bview,cursor.par(),
1080 rowLast(cursor.row()) + 1);
1086 void LyXText::cursorTop(BufferView * bview) const
1088 while (cursor.par()->previous())
1089 cursor.par(cursor.par()->previous());
1090 setCursor(bview, cursor.par(), 0);
1094 void LyXText::cursorBottom(BufferView * bview) const
1096 while (cursor.par()->next())
1097 cursor.par(cursor.par()->next());
1098 setCursor(bview, cursor.par(), cursor.par()->size());
1102 void LyXText::toggleFree(BufferView * bview,
1103 LyXFont const & font, bool toggleall)
1105 // If the mask is completely neutral, tell user
1106 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1107 // Could only happen with user style
1108 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1112 // Try implicit word selection
1113 // If there is a change in the language the implicit word selection
1115 LyXCursor resetCursor = cursor;
1116 bool implicitSelection = (font.language() == ignore_language
1117 && font.number() == LyXFont::IGNORE)
1118 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1121 setFont(bview, font, toggleall);
1123 // Implicit selections are cleared afterwards
1124 //and cursor is set to the original position.
1125 if (implicitSelection) {
1127 cursor = resetCursor;
1128 setCursor(bview, cursor.par(), cursor.pos());
1129 selection.cursor = cursor;
1132 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1137 LyXText::getStringToIndex(BufferView * bview)
1141 // Try implicit word selection
1142 // If there is a change in the language the implicit word selection
1144 LyXCursor resetCursor = cursor;
1145 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1147 if (!selection.set()) {
1148 bview->owner()->message(_("Nothing to index!"));
1151 if (selection.start.par() != selection.end.par()) {
1152 bview->owner()->message(_("Cannot index more than one paragraph!"));
1156 idxstring = selectionAsString(bview->buffer());
1158 // Implicit selections are cleared afterwards
1159 //and cursor is set to the original position.
1160 if (implicitSelection) {
1162 cursor = resetCursor;
1163 setCursor(bview, cursor.par(), cursor.pos());
1164 selection.cursor = cursor;
1169 Paragraph::size_type
1170 LyXText::beginningOfMainBody(Buffer const * buf,
1171 Paragraph const * par) const
1173 if (textclasslist.Style(buf->params.textclass,
1174 par->getLayout()).labeltype != LABEL_MANUAL)
1177 return par->beginningOfMainBody();
1181 /* the DTP switches for paragraphs. LyX will store them in the
1182 * first physicla paragraph. When a paragraph is broken, the top settings
1183 * rest, the bottom settings are given to the new one. So I can make shure,
1184 * they do not duplicate themself and you cannnot make dirty things with
1187 void LyXText::setParagraph(BufferView * bview,
1188 bool line_top, bool line_bottom,
1189 bool pagebreak_top, bool pagebreak_bottom,
1190 VSpace const & space_top,
1191 VSpace const & space_bottom,
1193 string labelwidthstring,
1196 LyXCursor tmpcursor = cursor;
1197 if (!selection.set()) {
1198 selection.start = cursor;
1199 selection.end = cursor;
1202 // make sure that the depth behind the selection are restored, too
1203 Paragraph * endpar = selection.end.par()->next();
1204 Paragraph * undoendpar = endpar;
1206 if (endpar && endpar->getDepth()) {
1207 while (endpar && endpar->getDepth()) {
1208 endpar = endpar->next();
1209 undoendpar = endpar;
1213 // because of parindents etc.
1214 endpar = endpar->next();
1217 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1220 Paragraph * tmppar = selection.end.par();
1221 while (tmppar != selection.start.par()->previous()) {
1222 setCursor(bview, tmppar, 0);
1223 status(bview, LyXText::NEED_MORE_REFRESH);
1224 refresh_row = cursor.row();
1225 refresh_y = cursor.y() - cursor.row()->baseline();
1226 cursor.par()->params().lineTop(line_top);
1227 cursor.par()->params().lineBottom(line_bottom);
1228 cursor.par()->params().pagebreakTop(pagebreak_top);
1229 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1230 cursor.par()->params().spaceTop(space_top);
1231 cursor.par()->params().spaceBottom(space_bottom);
1232 // does the layout allow the new alignment?
1233 if (align == LYX_ALIGN_LAYOUT)
1234 align = textclasslist
1235 .Style(bview->buffer()->params.textclass,
1236 cursor.par()->getLayout()).align;
1237 if (align & textclasslist
1238 .Style(bview->buffer()->params.textclass,
1239 cursor.par()->getLayout()).alignpossible) {
1240 if (align == textclasslist
1241 .Style(bview->buffer()->params.textclass,
1242 cursor.par()->getLayout()).align)
1243 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1245 cursor.par()->params().align(align);
1247 cursor.par()->setLabelWidthString(labelwidthstring);
1248 cursor.par()->params().noindent(noindent);
1249 tmppar = cursor.par()->previous();
1252 redoParagraphs(bview, selection.start, endpar);
1255 setCursor(bview, selection.start.par(), selection.start.pos());
1256 selection.cursor = cursor;
1257 setCursor(bview, selection.end.par(), selection.end.pos());
1258 setSelection(bview);
1259 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1261 bview->updateInset(inset_owner, true);
1265 char loweralphaCounter(int n)
1267 if (n < 1 || n > 26)
1277 char alphaCounter(int n)
1279 if (n < 1 || n > 26)
1287 char hebrewCounter(int n)
1289 static const char hebrew[22] = {
1290 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1291 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1292 '÷', 'ø', 'ù', 'ú'
1294 if (n < 1 || n > 22)
1302 string const romanCounter(int n)
1304 static char const * roman[20] = {
1305 "i", "ii", "iii", "iv", "v",
1306 "vi", "vii", "viii", "ix", "x",
1307 "xi", "xii", "xiii", "xiv", "xv",
1308 "xvi", "xvii", "xviii", "xix", "xx"
1310 if (n < 1 || n > 20)
1319 // set the counter of a paragraph. This includes the labels
1320 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1322 LyXLayout const & layout =
1323 textclasslist.Style(buf->params.textclass,
1326 LyXTextClass const & textclass =
1327 textclasslist.TextClass(buf->params.textclass);
1329 // copy the prev-counters to this one,
1330 // unless this is the first paragraph
1331 if (par->previous()) {
1332 for (int i = 0; i < 10; ++i) {
1333 par->setCounter(i, par->previous()->getFirstCounter(i));
1335 par->params().appendix(par->previous()->params().appendix());
1336 if (!par->params().appendix() && par->params().startOfAppendix()) {
1337 par->params().appendix(true);
1338 for (int i = 0; i < 10; ++i) {
1339 par->setCounter(i, 0);
1342 par->enumdepth = par->previous()->enumdepth;
1343 par->itemdepth = par->previous()->itemdepth;
1345 for (int i = 0; i < 10; ++i) {
1346 par->setCounter(i, 0);
1348 par->params().appendix(par->params().startOfAppendix());
1353 /* Maybe we have to increment the enumeration depth.
1354 * BUT, enumeration in a footnote is considered in isolation from its
1355 * surrounding paragraph so don't increment if this is the
1356 * first line of the footnote
1357 * AND, bibliographies can't have their depth changed ie. they
1358 * are always of depth 0
1361 && par->previous()->getDepth() < par->getDepth()
1362 && textclasslist.Style(buf->params.textclass,
1363 par->previous()->getLayout()
1364 ).labeltype == LABEL_COUNTER_ENUMI
1365 && par->enumdepth < 3
1366 && layout.labeltype != LABEL_BIBLIO) {
1370 // Maybe we have to decrement the enumeration depth, see note above
1372 && par->previous()->getDepth() > par->getDepth()
1373 && layout.labeltype != LABEL_BIBLIO) {
1374 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1375 par->setCounter(6 + par->enumdepth,
1376 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1377 /* reset the counters.
1378 * A depth change is like a breaking layout
1380 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1381 par->setCounter(i, 0);
1384 if (!par->params().labelString().empty()) {
1385 par->params().labelString(string());
1388 if (layout.margintype == MARGIN_MANUAL) {
1389 if (par->params().labelWidthString().empty()) {
1390 par->setLabelWidthString(layout.labelstring());
1393 par->setLabelWidthString(string());
1396 // is it a layout that has an automatic label?
1397 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1399 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1400 if (i >= 0 && i<= buf->params.secnumdepth) {
1401 par->incCounter(i); // increment the counter
1403 // Is there a label? Useful for Chapter layout
1404 if (!par->params().appendix()) {
1405 if (!layout.labelstring().empty())
1406 par->params().labelString(layout.labelstring());
1408 par->params().labelString(string());
1410 if (!layout.labelstring_appendix().empty())
1411 par->params().labelString(layout.labelstring_appendix());
1413 par->params().labelString(string());
1418 if (!par->params().appendix()) {
1419 switch (2 * LABEL_COUNTER_CHAPTER -
1420 textclass.maxcounter() + i) {
1421 case LABEL_COUNTER_CHAPTER:
1422 s << par->getCounter(i);
1424 case LABEL_COUNTER_SECTION:
1425 s << par->getCounter(i - 1) << '.'
1426 << par->getCounter(i);
1428 case LABEL_COUNTER_SUBSECTION:
1429 s << par->getCounter(i - 2) << '.'
1430 << par->getCounter(i - 1) << '.'
1431 << par->getCounter(i);
1433 case LABEL_COUNTER_SUBSUBSECTION:
1434 s << par->getCounter(i - 3) << '.'
1435 << par->getCounter(i - 2) << '.'
1436 << par->getCounter(i - 1) << '.'
1437 << par->getCounter(i);
1440 case LABEL_COUNTER_PARAGRAPH:
1441 s << par->getCounter(i - 4) << '.'
1442 << par->getCounter(i - 3) << '.'
1443 << par->getCounter(i - 2) << '.'
1444 << par->getCounter(i - 1) << '.'
1445 << par->getCounter(i);
1447 case LABEL_COUNTER_SUBPARAGRAPH:
1448 s << par->getCounter(i - 5) << '.'
1449 << par->getCounter(i - 4) << '.'
1450 << par->getCounter(i - 3) << '.'
1451 << par->getCounter(i - 2) << '.'
1452 << par->getCounter(i - 1) << '.'
1453 << par->getCounter(i);
1457 // Can this ever be reached? And in the
1458 // case it is, how can this be correct?
1460 s << par->getCounter(i) << '.';
1463 } else { // appendix
1464 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1465 case LABEL_COUNTER_CHAPTER:
1466 if (par->isRightToLeftPar(buf->params))
1467 s << hebrewCounter(par->getCounter(i));
1469 s << alphaCounter(par->getCounter(i));
1471 case LABEL_COUNTER_SECTION:
1472 if (par->isRightToLeftPar(buf->params))
1473 s << hebrewCounter(par->getCounter(i - 1));
1475 s << alphaCounter(par->getCounter(i - 1));
1478 << par->getCounter(i);
1481 case LABEL_COUNTER_SUBSECTION:
1482 if (par->isRightToLeftPar(buf->params))
1483 s << hebrewCounter(par->getCounter(i - 2));
1485 s << alphaCounter(par->getCounter(i - 2));
1488 << par->getCounter(i-1) << '.'
1489 << par->getCounter(i);
1492 case LABEL_COUNTER_SUBSUBSECTION:
1493 if (par->isRightToLeftPar(buf->params))
1494 s << hebrewCounter(par->getCounter(i-3));
1496 s << alphaCounter(par->getCounter(i-3));
1499 << par->getCounter(i-2) << '.'
1500 << par->getCounter(i-1) << '.'
1501 << par->getCounter(i);
1504 case LABEL_COUNTER_PARAGRAPH:
1505 if (par->isRightToLeftPar(buf->params))
1506 s << hebrewCounter(par->getCounter(i-4));
1508 s << alphaCounter(par->getCounter(i-4));
1511 << par->getCounter(i-3) << '.'
1512 << par->getCounter(i-2) << '.'
1513 << par->getCounter(i-1) << '.'
1514 << par->getCounter(i);
1517 case LABEL_COUNTER_SUBPARAGRAPH:
1518 if (par->isRightToLeftPar(buf->params))
1519 s << hebrewCounter(par->getCounter(i-5));
1521 s << alphaCounter(par->getCounter(i-5));
1524 << par->getCounter(i-4) << '.'
1525 << par->getCounter(i-3) << '.'
1526 << par->getCounter(i-2) << '.'
1527 << par->getCounter(i-1) << '.'
1528 << par->getCounter(i);
1532 // Can this ever be reached? And in the
1533 // case it is, how can this be correct?
1535 s << par->getCounter(i) << '.';
1541 par->params().labelString(par->params().labelString() +s.str().c_str());
1542 // We really want to remove the c_str as soon as
1545 for (i++; i < 10; ++i) {
1546 // reset the following counters
1547 par->setCounter(i, 0);
1549 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1550 for (i++; i < 10; ++i) {
1551 // reset the following counters
1552 par->setCounter(i, 0);
1554 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1555 par->incCounter(i + par->enumdepth);
1556 int number = par->getCounter(i + par->enumdepth);
1560 switch (par->enumdepth) {
1562 if (par->isRightToLeftPar(buf->params))
1564 << hebrewCounter(number)
1568 << loweralphaCounter(number)
1572 if (par->isRightToLeftPar(buf->params))
1573 s << '.' << romanCounter(number);
1575 s << romanCounter(number) << '.';
1578 if (par->isRightToLeftPar(buf->params))
1580 << alphaCounter(number);
1582 s << alphaCounter(number)
1586 if (par->isRightToLeftPar(buf->params))
1593 par->params().labelString(s.str().c_str());
1595 for (i += par->enumdepth + 1; i < 10; ++i) {
1596 // reset the following counters
1597 par->setCounter(i, 0);
1601 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1602 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1604 int number = par->getCounter(i);
1606 InsetCommandParams p( "bibitem" );
1607 par->bibkey = new InsetBibKey(p);
1609 par->bibkey->setCounter(number);
1610 par->params().labelString(layout.labelstring());
1612 // In biblio should't be following counters but...
1614 string s = layout.labelstring();
1616 // the caption hack:
1617 if (layout.labeltype == LABEL_SENSITIVE) {
1618 bool isOK (par->inInset() && par->inInset()->owner() &&
1619 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1622 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1624 = floatList.getType(tmp->type());
1625 // We should get the correct number here too.
1626 s = fl.name() + " #:";
1628 /* par->SetLayout(0);
1629 s = layout->labelstring; */
1630 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1631 ? " :úåòîùî øñç" : "Senseless: ";
1634 par->params().labelString(s);
1636 /* reset the enumeration counter. They are always resetted
1637 * when there is any other layout between */
1638 for (int i = 6 + par->enumdepth; i < 10; ++i)
1639 par->setCounter(i, 0);
1644 // Updates all counters BEHIND the row. Changed paragraphs
1645 // with a dynamic left margin will be rebroken.
1646 void LyXText::updateCounters(BufferView * bview, Row * row) const
1654 par = row->par()->next();
1658 while (row->par() != par)
1661 setCounter(bview->buffer(), par);
1663 // now check for the headline layouts. remember that they
1664 // have a dynamic left margin
1665 if ((textclasslist.Style(bview->buffer()->params.textclass,
1666 par->layout).margintype == MARGIN_DYNAMIC
1667 || textclasslist.Style(bview->buffer()->params.textclass,
1668 par->layout).labeltype == LABEL_SENSITIVE)) {
1670 // Rebreak the paragraph
1671 removeParagraph(row);
1672 appendParagraph(bview, row);
1679 void LyXText::insertInset(BufferView * bview, Inset * inset)
1681 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1683 setUndo(bview, Undo::INSERT,
1684 cursor.par(), cursor.par()->next());
1685 cursor.par()->insertInset(cursor.pos(), inset);
1686 // Just to rebreak and refresh correctly.
1687 // The character will not be inserted a second time
1688 insertChar(bview, Paragraph::META_INSET);
1690 // If we enter a highly editable inset the cursor should be to before
1691 // the inset. This couldn't happen before as Undo was not handled inside
1692 // inset now after the Undo LyX tries to call inset->Edit(...) again
1693 // and cannot do this as the cursor is behind the inset and GetInset
1694 // does not return the inset!
1695 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1696 cursorLeft(bview, true);
1702 void LyXText::copyEnvironmentType()
1704 copylayouttype = cursor.par()->getLayout();
1708 void LyXText::pasteEnvironmentType(BufferView * bview)
1710 setLayout(bview, copylayouttype);
1714 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1716 // Stuff what we got on the clipboard. Even if there is no selection.
1718 // There is a problem with having the stuffing here in that the
1719 // larger the selection the slower LyX will get. This can be
1720 // solved by running the line below only when the selection has
1721 // finished. The solution used currently just works, to make it
1722 // faster we need to be more clever and probably also have more
1723 // calls to stuffClipboard. (Lgb)
1724 bview->stuffClipboard(selectionAsString(bview->buffer()));
1726 // This doesn't make sense, if there is no selection
1727 if (!selection.set())
1730 // OK, we have a selection. This is always between selection.start
1731 // and selection.end
1733 // make sure that the depth behind the selection are restored, too
1734 Paragraph * endpar = selection.end.par()->next();
1735 Paragraph * undoendpar = endpar;
1737 if (endpar && endpar->getDepth()) {
1738 while (endpar && endpar->getDepth()) {
1739 endpar = endpar->next();
1740 undoendpar = endpar;
1742 } else if (endpar) {
1743 endpar = endpar->next(); // because of parindents etc.
1746 setUndo(bview, Undo::DELETE,
1747 selection.start.par(), undoendpar);
1749 // there are two cases: cut only within one paragraph or
1750 // more than one paragraph
1751 if (selection.start.par() == selection.end.par()) {
1752 // only within one paragraph
1753 endpar = selection.end.par();
1754 int pos = selection.end.pos();
1755 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1756 selection.start.pos(), pos,
1757 bview->buffer()->params.textclass, doclear,
1759 selection.end.pos(pos);
1761 endpar = selection.end.par();
1762 int pos = selection.end.pos();
1763 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1764 selection.start.pos(), pos,
1765 bview->buffer()->params.textclass, doclear,
1768 selection.end.par(endpar);
1769 selection.end.pos(pos);
1770 cursor.pos(selection.end.pos());
1772 endpar = endpar->next();
1774 // sometimes necessary
1776 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1778 redoParagraphs(bview, selection.start, endpar);
1780 // cutSelection can invalidate the cursor so we need to set
1782 cursor = selection.start;
1784 // need a valid cursor. (Lgb)
1787 setCursor(bview, cursor.par(), cursor.pos());
1788 selection.cursor = cursor;
1789 updateCounters(bview, cursor.row());
1793 void LyXText::copySelection(BufferView * bview)
1795 // Stuff what we got on the clipboard. Even if there is no selection.
1797 // There is a problem with having the stuffing here in that the
1798 // larger the selection the slower LyX will get. This can be
1799 // solved by running the line below only when the selection has
1800 // finished. The solution used currently just works, to make it
1801 // faster we need to be more clever and probably also have more
1802 // calls to stuffClipboard. (Lgb)
1803 bview->stuffClipboard(selectionAsString(bview->buffer()));
1805 // this doesnt make sense, if there is no selection
1806 if (!selection.set())
1809 // ok we have a selection. This is always between selection.start
1810 // and sel_end cursor
1812 // copy behind a space if there is one
1813 while (selection.start.par()->size() > selection.start.pos()
1814 && selection.start.par()->isLineSeparator(selection.start.pos())
1815 && (selection.start.par() != selection.end.par()
1816 || selection.start.pos() < selection.end.pos()))
1817 selection.start.pos(selection.start.pos() + 1);
1819 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1820 selection.start.pos(), selection.end.pos(),
1821 bview->buffer()->params.textclass);
1825 void LyXText::pasteSelection(BufferView * bview)
1827 // this does not make sense, if there is nothing to paste
1828 if (!CutAndPaste::checkPastePossible(cursor.par()))
1831 setUndo(bview, Undo::INSERT,
1832 cursor.par(), cursor.par()->next());
1835 Paragraph * actpar = cursor.par();
1837 int pos = cursor.pos();
1838 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1839 bview->buffer()->params.textclass);
1841 redoParagraphs(bview, cursor, endpar);
1843 setCursor(bview, cursor.par(), cursor.pos());
1846 selection.cursor = cursor;
1847 setCursor(bview, actpar, pos);
1848 setSelection(bview);
1849 updateCounters(bview, cursor.row());
1853 // returns a pointer to the very first Paragraph
1854 Paragraph * LyXText::firstParagraph() const
1856 return ownerParagraph();
1860 // sets the selection over the number of characters of string, no check!!
1861 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1866 selection.cursor = cursor;
1867 for (string::size_type i = 0; i < str.length(); ++i)
1869 setSelection(bview);
1873 // simple replacing. The font of the first selected character is used
1874 void LyXText::replaceSelectionWithString(BufferView * bview,
1877 setCursorParUndo(bview);
1880 if (!selection.set()) { // create a dummy selection
1881 selection.end = cursor;
1882 selection.start = cursor;
1885 // Get font setting before we cut
1886 Paragraph::size_type pos = selection.end.pos();
1887 LyXFont const font = selection.start.par()
1888 ->getFontSettings(bview->buffer()->params,
1889 selection.start.pos());
1891 // Insert the new string
1892 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1893 selection.end.par()->insertChar(pos, (*cit), font);
1897 // Cut the selection
1898 cutSelection(bview, true, false);
1904 // needed to insert the selection
1905 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1907 Paragraph * par = cursor.par();
1908 Paragraph::size_type pos = cursor.pos();
1909 Paragraph * endpar = cursor.par()->next();
1911 setCursorParUndo(bview);
1913 // only to be sure, should not be neccessary
1916 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1918 redoParagraphs(bview, cursor, endpar);
1919 setCursor(bview, cursor.par(), cursor.pos());
1920 selection.cursor = cursor;
1921 setCursor(bview, par, pos);
1922 setSelection(bview);
1926 // turns double-CR to single CR, others where converted into one
1927 // blank. Then InsertStringAsLines is called
1928 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1930 string linestr(str);
1931 bool newline_inserted = false;
1932 for (string::size_type i = 0; i < linestr.length(); ++i) {
1933 if (linestr[i] == '\n') {
1934 if (newline_inserted) {
1935 // we know that \r will be ignored by
1936 // InsertStringA. Of course, it is a dirty
1937 // trick, but it works...
1938 linestr[i - 1] = '\r';
1942 newline_inserted = true;
1944 } else if (IsPrintable(linestr[i])) {
1945 newline_inserted = false;
1948 insertStringAsLines(bview, linestr);
1952 bool LyXText::gotoNextInset(BufferView * bview,
1953 std::vector<Inset::Code> const & codes,
1954 string const & contents) const
1956 LyXCursor res = cursor;
1959 if (res.pos() < res.par()->size() - 1) {
1960 res.pos(res.pos() + 1);
1962 res.par(res.par()->next());
1966 } while (res.par() &&
1967 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1968 && (inset = res.par()->getInset(res.pos())) != 0
1969 && find(codes.begin(), codes.end(), inset->lyxCode())
1971 && (contents.empty() ||
1972 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1976 setCursor(bview, res.par(), res.pos());
1983 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1984 Paragraph::size_type pos)
1986 LyXCursor tmpcursor;
1989 Paragraph::size_type z;
1990 Row * row = getRow(par, pos, y);
1992 // is there a break one row above
1993 if (row->previous() && row->previous()->par() == row->par()) {
1994 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1995 if (z >= row->pos()) {
1996 // set the dimensions of the row above
1997 y -= row->previous()->height();
1999 refresh_row = row->previous();
2000 status(bview, LyXText::NEED_MORE_REFRESH);
2002 breakAgain(bview, row->previous());
2004 // set the cursor again. Otherwise
2005 // dangling pointers are possible
2006 setCursor(bview, cursor.par(), cursor.pos(),
2007 false, cursor.boundary());
2008 selection.cursor = cursor;
2013 int const tmpheight = row->height();
2014 Paragraph::size_type const tmplast = rowLast(row);
2018 breakAgain(bview, row);
2019 if (row->height() == tmpheight && rowLast(row) == tmplast)
2020 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2022 status(bview, LyXText::NEED_MORE_REFRESH);
2024 // check the special right address boxes
2025 if (textclasslist.Style(bview->buffer()->params.textclass,
2026 par->getLayout()).margintype
2027 == MARGIN_RIGHT_ADDRESS_BOX) {
2034 redoDrawingOfParagraph(bview, tmpcursor);
2037 // set the cursor again. Otherwise dangling pointers are possible
2038 // also set the selection
2040 if (selection.set()) {
2042 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2043 false, selection.cursor.boundary());
2044 selection.cursor = cursor;
2045 setCursorIntern(bview, selection.start.par(),
2046 selection.start.pos(),
2047 false, selection.start.boundary());
2048 selection.start = cursor;
2049 setCursorIntern(bview, selection.end.par(),
2050 selection.end.pos(),
2051 false, selection.end.boundary());
2052 selection.end = cursor;
2053 setCursorIntern(bview, last_sel_cursor.par(),
2054 last_sel_cursor.pos(),
2055 false, last_sel_cursor.boundary());
2056 last_sel_cursor = cursor;
2059 setCursorIntern(bview, cursor.par(), cursor.pos(),
2060 false, cursor.boundary());
2064 // returns false if inset wasn't found
2065 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2067 // first check the current paragraph
2068 int pos = cursor.par()->getPositionOfInset(inset);
2070 checkParagraph(bview, cursor.par(), pos);
2074 // check every paragraph
2076 Paragraph * par = firstParagraph();
2078 pos = par->getPositionOfInset(inset);
2080 checkParagraph(bview, par, pos);
2090 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2091 Paragraph::size_type pos,
2092 bool setfont, bool boundary) const
2094 LyXCursor old_cursor = cursor;
2095 setCursorIntern(bview, par, pos, setfont, boundary);
2096 deleteEmptyParagraphMechanism(bview, old_cursor);
2100 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2101 Paragraph::size_type pos, bool boundary) const
2105 cur.boundary(boundary);
2107 // get the cursor y position in text
2109 Row * row = getRow(par, pos, y);
2110 // y is now the beginning of the cursor row
2111 y += row->baseline();
2112 // y is now the cursor baseline
2115 // now get the cursors x position
2117 float fill_separator;
2119 float fill_label_hfill;
2120 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2122 Paragraph::size_type cursor_vpos = 0;
2123 Paragraph::size_type last = rowLastPrintable(row);
2125 if (pos > last + 1) // This shouldn't happen.
2127 else if (pos < row->pos())
2130 if (last < row->pos())
2131 cursor_vpos = row->pos();
2132 else if (pos > last && !boundary)
2133 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2134 ? row->pos() : last + 1;
2135 else if (pos > row->pos() &&
2136 (pos > last || boundary))
2137 /// Place cursor after char at (logical) position pos - 1
2138 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2139 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2141 /// Place cursor before char at (logical) position pos
2142 cursor_vpos = (bidi_level(pos) % 2 == 0)
2143 ? log2vis(pos) : log2vis(pos) + 1;
2145 Paragraph::size_type main_body =
2146 beginningOfMainBody(bview->buffer(), row->par());
2147 if ((main_body > 0) &&
2148 ((main_body-1 > last) ||
2149 !row->par()->isLineSeparator(main_body-1)))
2152 for (Paragraph::size_type vpos = row->pos();
2153 vpos < cursor_vpos; ++vpos) {
2154 pos = vis2log(vpos);
2155 if (main_body > 0 && pos == main_body - 1) {
2156 x += fill_label_hfill +
2157 lyxfont::width(textclasslist.Style(
2158 bview->buffer()->params.textclass,
2159 row->par()->getLayout())
2161 getLabelFont(bview->buffer(), row->par()));
2162 if (row->par()->isLineSeparator(main_body-1))
2163 x -= singleWidth(bview, row->par(),main_body-1);
2165 if (hfillExpansion(bview->buffer(), row, pos)) {
2166 x += singleWidth(bview, row->par(), pos);
2167 if (pos >= main_body)
2170 x += fill_label_hfill;
2171 } else if (row->par()->isSeparator(pos)) {
2172 x += singleWidth(bview, row->par(), pos);
2173 if (pos >= main_body)
2174 x += fill_separator;
2176 x += singleWidth(bview, row->par(), pos);
2185 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2186 Paragraph::size_type pos,
2187 bool setfont, bool boundary) const
2189 InsetText * it = static_cast<InsetText *>(par->inInset());
2191 if (it != inset_owner) {
2192 lyxerr << "InsetText is " << it << endl;
2193 lyxerr << "inset_owner is " << inset_owner << endl;
2194 #warning I belive this code is wrong. (Lgb)
2195 #warning Jürgen, have a look at this. (Lgb)
2196 #warning Hmmm, I guess you are right but we
2197 #warning should verify when this is needed
2198 // Jürgen, would you like to have a look?
2199 // I guess we need to move the outer cursor
2200 // and open and lock the inset (bla bla bla)
2201 // stuff I don't know... so can you have a look?
2203 // I moved the lyxerr stuff in here so we can see if this
2204 // is actually really needed and where!
2206 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2212 setCursor(bview, cursor, par, pos, boundary);
2214 setCurrentFont(bview);
2218 void LyXText::setCurrentFont(BufferView * bview) const
2220 Paragraph::size_type pos = cursor.pos();
2221 if (cursor.boundary() && pos > 0)
2225 if (pos == cursor.par()->size())
2227 else // potentional bug... BUG (Lgb)
2228 if (cursor.par()->isSeparator(pos)) {
2229 if (pos > cursor.row()->pos() &&
2230 bidi_level(pos) % 2 ==
2231 bidi_level(pos - 1) % 2)
2233 else if (pos + 1 < cursor.par()->size())
2239 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2240 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2242 if (cursor.pos() == cursor.par()->size() &&
2243 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2244 !cursor.boundary()) {
2245 Language const * lang =
2246 cursor.par()->getParLanguage(bview->buffer()->params);
2247 current_font.setLanguage(lang);
2248 current_font.setNumber(LyXFont::OFF);
2249 real_current_font.setLanguage(lang);
2250 real_current_font.setNumber(LyXFont::OFF);
2255 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2257 LyXCursor old_cursor = cursor;
2259 setCursorFromCoordinates(bview, cursor, x, y);
2260 setCurrentFont(bview);
2261 deleteEmptyParagraphMechanism(bview, old_cursor);
2265 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2268 // Get the row first.
2270 Row * row = getRowNearY(y);
2272 int const column = getColumnNearX(bview, row, x, bound);
2274 cur.par(row->par());
2275 cur.pos(row->pos() + column);
2277 cur.y(y + row->baseline());
2279 cur.boundary(bound);
2283 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2285 if (cursor.pos() > 0) {
2286 bool boundary = cursor.boundary();
2287 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2288 if (!internal && !boundary &&
2289 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2290 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2291 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2292 Paragraph * par = cursor.par()->previous();
2293 setCursor(bview, par, par->size());
2298 void LyXText::cursorRight(BufferView * bview, bool internal) const
2300 if (!internal && cursor.boundary() &&
2301 !cursor.par()->isNewline(cursor.pos()))
2302 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2303 else if (cursor.pos() < cursor.par()->size()) {
2304 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2306 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2307 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2308 } else if (cursor.par()->next())
2309 setCursor(bview, cursor.par()->next(), 0);
2313 void LyXText::cursorUp(BufferView * bview) const
2315 setCursorFromCoordinates(bview, cursor.x_fix(),
2316 cursor.y() - cursor.row()->baseline() - 1);
2320 void LyXText::cursorDown(BufferView * bview) const
2322 setCursorFromCoordinates(bview, cursor.x_fix(),
2323 cursor.y() - cursor.row()->baseline()
2324 + cursor.row()->height() + 1);
2328 void LyXText::cursorUpParagraph(BufferView * bview) const
2330 if (cursor.pos() > 0) {
2331 setCursor(bview, cursor.par(), 0);
2333 else if (cursor.par()->previous()) {
2334 setCursor(bview, cursor.par()->previous(), 0);
2339 void LyXText::cursorDownParagraph(BufferView * bview) const
2341 if (cursor.par()->next()) {
2342 setCursor(bview, cursor.par()->next(), 0);
2344 setCursor(bview, cursor.par(), cursor.par()->size());
2349 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2350 LyXCursor const & old_cursor) const
2352 // Would be wrong to delete anything if we have a selection.
2353 if (selection.set()) return;
2355 // We allow all kinds of "mumbo-jumbo" when freespacing.
2356 if (textclasslist.Style(bview->buffer()->params.textclass,
2357 old_cursor.par()->getLayout()).free_spacing)
2360 bool deleted = false;
2362 /* Ok I'll put some comments here about what is missing.
2363 I have fixed BackSpace (and thus Delete) to not delete
2364 double-spaces automagically. I have also changed Cut,
2365 Copy and Paste to hopefully do some sensible things.
2366 There are still some small problems that can lead to
2367 double spaces stored in the document file or space at
2368 the beginning of paragraphs. This happens if you have
2369 the cursor betwenn to spaces and then save. Or if you
2370 cut and paste and the selection have a space at the
2371 beginning and then save right after the paste. I am
2372 sure none of these are very hard to fix, but I will
2373 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2374 that I can get some feedback. (Lgb)
2377 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2378 // delete the LineSeparator.
2381 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2382 // delete the LineSeparator.
2385 // If the pos around the old_cursor were spaces, delete one of them.
2386 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2387 // Only if the cursor has really moved
2389 if (old_cursor.pos() > 0
2390 && old_cursor.pos() < old_cursor.par()->size()
2391 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2392 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2393 old_cursor.par()->erase(old_cursor.pos() - 1);
2394 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2396 if (old_cursor.par() == cursor.par() &&
2397 cursor.pos() > old_cursor.pos()) {
2398 setCursorIntern(bview, cursor.par(),
2401 setCursorIntern(bview, cursor.par(),
2407 // Do not delete empty paragraphs with keepempty set.
2408 if ((textclasslist.Style(bview->buffer()->params.textclass,
2409 old_cursor.par()->getLayout())).keepempty)
2412 LyXCursor tmpcursor;
2414 if (old_cursor.par() != cursor.par()) {
2415 if ((old_cursor.par()->size() == 0
2416 || (old_cursor.par()->size() == 1
2417 && old_cursor.par()->isLineSeparator(0)))) {
2418 // ok, we will delete anything
2420 // make sure that you do not delete any environments
2421 status(bview, LyXText::NEED_MORE_REFRESH);
2424 if (old_cursor.row()->previous()) {
2425 refresh_row = old_cursor.row()->previous();
2426 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2428 cursor = old_cursor; // that undo can restore the right cursor position
2429 Paragraph * endpar = old_cursor.par()->next();
2430 if (endpar && endpar->getDepth()) {
2431 while (endpar && endpar->getDepth()) {
2432 endpar = endpar->next();
2435 setUndo(bview, Undo::DELETE,
2441 removeRow(old_cursor.row());
2442 if (ownerParagraph() == old_cursor.par()) {
2443 ownerParagraph(ownerParagraph()->next());
2446 delete old_cursor.par();
2448 /* Breakagain the next par. Needed
2449 * because of the parindent that
2450 * can occur or dissappear. The
2451 * next row can change its height,
2452 * if there is another layout before */
2453 if (refresh_row->next()) {
2454 breakAgain(bview, refresh_row->next());
2455 updateCounters(bview, refresh_row);
2457 setHeightOfRow(bview, refresh_row);
2459 refresh_row = old_cursor.row()->next();
2460 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2463 cursor = old_cursor; // that undo can restore the right cursor position
2464 Paragraph * endpar = old_cursor.par()->next();
2465 if (endpar && endpar->getDepth()) {
2466 while (endpar && endpar->getDepth()) {
2467 endpar = endpar->next();
2470 setUndo(bview, Undo::DELETE,
2476 removeRow(old_cursor.row());
2478 if (ownerParagraph() == old_cursor.par()) {
2479 ownerParagraph(ownerParagraph()->next());
2482 delete old_cursor.par();
2484 /* Breakagain the next par. Needed
2485 because of the parindent that can
2486 occur or dissappear.
2487 The next row can change its height,
2488 if there is another layout before
2491 breakAgain(bview, refresh_row);
2492 updateCounters(bview, refresh_row->previous());
2498 setCursorIntern(bview, cursor.par(), cursor.pos());
2500 if (selection.cursor.par() == old_cursor.par()
2501 && selection.cursor.pos() == selection.cursor.pos()) {
2502 // correct selection
2503 selection.cursor = cursor;
2507 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2508 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2510 setCursorIntern(bview, cursor.par(), cursor.pos());
2511 selection.cursor = cursor;
2518 void LyXText::toggleAppendix(BufferView * bview)
2520 Paragraph * par = cursor.par();
2521 bool start = !par->params().startOfAppendix();
2523 // ensure that we have only one start_of_appendix in this document
2524 Paragraph * tmp = firstParagraph();
2525 for (; tmp; tmp = tmp->next()) {
2526 tmp->params().startOfAppendix(false);
2529 par->params().startOfAppendix(start);
2531 // we can set the refreshing parameters now
2532 status(bview, LyXText::NEED_MORE_REFRESH);
2534 refresh_row = 0; // not needed for full update
2535 updateCounters(bview, 0);
2536 setCursor(bview, cursor.par(), cursor.pos());
2540 Paragraph * LyXText::ownerParagraph() const
2543 return inset_owner->paragraph();
2545 return bv_owner->buffer()->paragraph;
2549 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2552 inset_owner->paragraph(p);
2554 bv_owner->buffer()->paragraph = p;
2559 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2561 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2562 if (op && op->inInset()) {
2563 static_cast<InsetText *>(op->inInset())->paragraph(p);
2566 inset_owner->paragraph(p);
2568 bv_owner->buffer()->paragraph = p;
2575 LyXText::text_status LyXText::status() const
2581 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2583 // well as much as I know && binds more then || so the above and the
2584 // below are identical (this for your known use of parentesis!)
2585 // Now some explanation:
2586 // We should only go up with refreshing code so this means that if
2587 // we have a MORE refresh we should never set it to LITTLE if we still
2588 // didn't handle it (and then it will be UNCHANGED. Now as long as
2589 // we stay inside one LyXText this may work but we need to tell the
2590 // outermost LyXText that it should REALLY draw us if there is some
2591 // change in a Inset::LyXText. So you see that when we are inside a
2592 // inset's LyXText we give the LITTLE to the outermost LyXText to
2593 // tell'em that it should redraw the actual row (where the inset
2594 // resides! Capito?!
2596 if ((status_ != NEED_MORE_REFRESH)
2597 || (status_ == NEED_MORE_REFRESH
2598 && st != NEED_VERY_LITTLE_REFRESH))
2601 if (inset_owner && st != UNCHANGED) {
2602 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2603 if (!bview->text->refresh_row) {
2604 bview->text->refresh_row = bview->text->cursor.row();
2605 bview->text->refresh_y = bview->text->cursor.y() -
2606 bview->text->cursor.row()->baseline();