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 tmpfont.realize(textclasslist.
124 Style(buf->params.textclass,
125 par->getLayout()).font,
126 buf->params.language);
127 par_depth = par->getDepth();
131 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
132 buf->params.language);
140 // Gets the fully instantiated font at a given position in a paragraph
141 // Basically the same routine as Paragraph::getFont() in paragraph.C.
142 // The difference is that this one is used for displaying, and thus we
143 // are allowed to make cosmetic improvements. For instance make footnotes
145 // If position is -1, we get the layout font of the paragraph.
146 // If position is -2, we get the font of the manual label of the paragraph.
147 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
148 Paragraph::size_type pos) const
150 lyx::Assert(pos >= 0);
152 LyXLayout const & layout =
153 textclasslist.Style(buf->params.textclass, par->getLayout());
155 Paragraph::depth_type par_depth = par->getDepth();
156 // We specialize the 95% common case:
158 if (layout.labeltype == LABEL_MANUAL
159 && pos < beginningOfMainBody(buf, par)) {
161 LyXFont f = par->getFontSettings(buf->params,
163 return f.realize(layout.reslabelfont, buf->params.language);
165 LyXFont f = par->getFontSettings(buf->params, pos);
166 return f.realize(layout.resfont, buf->params.language);
170 // The uncommon case need not be optimized as much
174 if (pos < beginningOfMainBody(buf, par)) {
176 layoutfont = layout.labelfont;
179 layoutfont = layout.font;
182 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
183 tmpfont.realize(layoutfont, buf->params.language);
185 return realizeFont(tmpfont, buf, par);
189 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
191 LyXLayout const & layout =
192 textclasslist.Style(buf->params.textclass, par->getLayout());
194 Paragraph::depth_type par_depth = par->getDepth();
197 return layout.resfont;
200 return realizeFont(layout.font, buf, par);
204 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
206 LyXLayout const & layout =
207 textclasslist.Style(buf->params.textclass, par->getLayout());
209 Paragraph::depth_type par_depth = par->getDepth();
212 return layout.reslabelfont;
215 return realizeFont(layout.labelfont, buf, par);
219 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
220 Paragraph::size_type pos, LyXFont const & fnt,
223 Buffer const * buf = bv->buffer();
224 LyXFont font = getFont(buf, par, pos);
225 font.update(fnt, toggleall);
226 // Let the insets convert their font
227 if (par->getChar(pos) == Paragraph::META_INSET) {
228 Inset * inset = par->getInset(pos);
230 if (inset->editable()==Inset::IS_EDITABLE) {
231 UpdatableInset * uinset =
232 static_cast<UpdatableInset *>(inset);
233 uinset->setFont(bv, fnt, toggleall, true);
238 LyXLayout const & layout =
239 textclasslist.Style(buf->params.textclass,
242 // Get concrete layout font to reduce against
245 if (pos < beginningOfMainBody(buf, par))
246 layoutfont = layout.labelfont;
248 layoutfont = layout.font;
250 // Realize against environment font information
251 if (par->getDepth()){
252 Paragraph * tp = par;
253 while (!layoutfont.resolved() && tp && tp->getDepth()) {
254 tp = tp->outerHook();
256 layoutfont.realize(textclasslist.
257 Style(buf->params.textclass,
258 tp->getLayout()).font,
259 buf->params.language);
263 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
264 buf->params.language);
266 // Now, reduce font against full layout font
267 font.reduce(layoutfont);
269 par->setFont(pos, font);
273 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
274 Paragraph::size_type pos, LyXFont const & fnt)
278 LyXLayout const & layout =
279 textclasslist.Style(buf->params.textclass,
282 // Get concrete layout font to reduce against
285 if (pos < beginningOfMainBody(buf, par))
286 layoutfont = layout.labelfont;
288 layoutfont = layout.font;
290 // Realize against environment font information
291 if (par->getDepth()){
292 Paragraph * tp = par;
293 while (!layoutfont.resolved() && tp && tp->getDepth()) {
294 tp = tp->outerHook();
296 layoutfont.realize(textclasslist.
297 Style(buf->params.textclass,
298 tp->getLayout()).font,
299 buf->params.language);
303 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
304 buf->params.language);
306 // Now, reduce font against full layout font
307 font.reduce(layoutfont);
309 par->setFont(pos, font);
313 // inserts a new row behind the specified row, increments
314 // the touched counters
315 void LyXText::insertRow(Row * row, Paragraph * par,
316 Paragraph::size_type pos) const
318 Row * tmprow = new Row;
321 tmprow->next(firstrow);
324 tmprow->previous(row);
325 tmprow->next(row->next());
330 tmprow->next()->previous(tmprow);
332 if (tmprow->previous())
333 tmprow->previous()->next(tmprow);
345 // removes the row and reset the touched counters
346 void LyXText::removeRow(Row * row) const
348 /* this must not happen before the currentrow for clear reasons.
349 so the trick is just to set the current row onto the previous
352 getRow(row->par(), row->pos(), unused_y);
355 row->next()->previous(row->previous());
356 if (!row->previous()) {
357 firstrow = row->next();
359 row->previous()->next(row->next());
362 lastrow = row->previous();
364 height -= row->height(); // the text becomes smaller
367 --number_of_rows; // one row less
371 // remove all following rows of the paragraph of the specified row.
372 void LyXText::removeParagraph(Row * row) const
374 Paragraph * tmppar = row->par();
378 while (row && row->par() == tmppar) {
379 tmprow = row->next();
386 // insert the specified paragraph behind the specified row
387 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
390 insertRow(row, par, 0); /* insert a new row, starting
393 setCounter(bview->buffer(), par); // set the counters
395 // and now append the whole paragraph behind the new row
398 appendParagraph(bview, firstrow);
400 row->next()->height(0);
401 appendParagraph(bview, row->next());
406 Inset * LyXText::getInset() const
409 if (cursor.pos() == 0 && cursor.par()->bibkey) {
410 inset = cursor.par()->bibkey;
411 } else if (cursor.pos() < cursor.par()->size()
412 && cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
413 inset = cursor.par()->getInset(cursor.pos());
419 void LyXText::toggleInset(BufferView * bview)
421 Inset * inset = getInset();
422 if (!inset->editable())
424 //bview->owner()->message(inset->editMessage());
426 // do we want to keep this?? (JMarc)
427 if (inset->editable() != Inset::HIGHLY_EDITABLE)
428 setCursorParUndo(bview);
430 if (inset->isOpen()) {
436 inset->open(bview, !inset->isOpen());
441 /* used in setlayout */
442 // Asger is not sure we want to do this...
443 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
446 LyXLayout const & layout =
447 textclasslist.Style(buf->params.textclass, par->getLayout());
450 for (Paragraph::size_type pos = 0; pos < par->size(); ++pos) {
451 if (pos < beginningOfMainBody(buf, par))
452 layoutfont = layout.labelfont;
454 layoutfont = layout.font;
456 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
457 tmpfont.reduce(layoutfont);
458 par->setFont(pos, tmpfont);
463 Paragraph * LyXText::setLayout(BufferView * bview,
464 LyXCursor & cur, LyXCursor & sstart_cur,
465 LyXCursor & send_cur,
466 LyXTextClass::size_type layout)
468 Paragraph * endpar = send_cur.par()->next();
469 Paragraph * undoendpar = endpar;
471 if (endpar && endpar->getDepth()) {
472 while (endpar && endpar->getDepth()) {
473 endpar = endpar->next();
477 endpar = endpar->next(); // because of parindents etc.
480 setUndo(bview, Undo::EDIT,
481 sstart_cur.par(), undoendpar);
483 // ok we have a selection. This is always between sstart_cur
484 // and sel_end cursor
487 LyXLayout const & lyxlayout =
488 textclasslist.Style(bview->buffer()->params.textclass, layout);
490 while (cur.par() != send_cur.par()) {
491 cur.par()->setLayout(layout);
492 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
493 Paragraph * fppar = cur.par();
494 fppar->params().spaceTop(lyxlayout.fill_top ?
495 VSpace(VSpace::VFILL)
496 : VSpace(VSpace::NONE));
497 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
498 VSpace(VSpace::VFILL)
499 : VSpace(VSpace::NONE));
500 if (lyxlayout.margintype == MARGIN_MANUAL)
501 cur.par()->setLabelWidthString(lyxlayout.labelstring());
502 if (lyxlayout.labeltype != LABEL_BIBLIO
504 delete fppar->bibkey;
507 cur.par(cur.par()->next());
509 cur.par()->setLayout(layout);
510 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
511 Paragraph * fppar = cur.par();
512 fppar->params().spaceTop(lyxlayout.fill_top ?
513 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
514 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
515 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
516 if (lyxlayout.margintype == MARGIN_MANUAL)
517 cur.par()->setLabelWidthString(lyxlayout.labelstring());
518 if (lyxlayout.labeltype != LABEL_BIBLIO
520 delete fppar->bibkey;
527 // set layout over selection and make a total rebreak of those paragraphs
528 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
530 LyXCursor tmpcursor = cursor; /* store the current cursor */
532 // if there is no selection just set the layout
533 // of the current paragraph */
534 if (!selection.set()) {
535 selection.start = cursor; // dummy selection
536 selection.end = cursor;
538 Paragraph * endpar = setLayout(bview, cursor, selection.start,
539 selection.end, layout);
540 redoParagraphs(bview, selection.start, endpar);
542 // we have to reset the selection, because the
543 // geometry could have changed
544 setCursor(bview, selection.start.par(),
545 selection.start.pos(), false);
546 selection.cursor = cursor;
547 setCursor(bview, selection.end.par(), selection.end.pos(), false);
548 updateCounters(bview, cursor.row());
551 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
555 // increment depth over selection and
556 // make a total rebreak of those paragraphs
557 void LyXText::incDepth(BufferView * bview)
559 // If there is no selection, just use the current paragraph
560 if (!selection.set()) {
561 selection.start = cursor; // dummy selection
562 selection.end = cursor;
565 // We end at the next paragraph with depth 0
566 Paragraph * endpar = selection.end.par()->next();
568 Paragraph * undoendpar = endpar;
570 if (endpar && endpar->getDepth()) {
571 while (endpar && endpar->getDepth()) {
572 endpar = endpar->next();
576 endpar = endpar->next(); // because of parindents etc.
579 setUndo(bview, Undo::EDIT,
580 selection.start.par(), undoendpar);
582 LyXCursor tmpcursor = cursor; // store the current cursor
584 // ok we have a selection. This is always between sel_start_cursor
585 // and sel_end cursor
586 cursor = selection.start;
588 bool anything_changed = false;
591 // NOTE: you can't change the depth of a bibliography entry
593 textclasslist.Style(bview->buffer()->params.textclass,
594 cursor.par()->getLayout()
595 ).labeltype != LABEL_BIBLIO) {
596 Paragraph * prev = cursor.par()->previous();
599 && (prev->getDepth() - cursor.par()->getDepth() > 0
600 || (prev->getDepth() == cursor.par()->getDepth()
601 && textclasslist.Style(bview->buffer()->params.textclass,
602 prev->getLayout()).isEnvironment()))) {
603 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
604 anything_changed = true;
607 if (cursor.par() == selection.end.par())
609 cursor.par(cursor.par()->next());
612 // if nothing changed set all depth to 0
613 if (!anything_changed) {
614 cursor = selection.start;
615 while (cursor.par() != selection.end.par()) {
616 cursor.par()->params().depth(0);
617 cursor.par(cursor.par()->next());
619 cursor.par()->params().depth(0);
622 redoParagraphs(bview, selection.start, endpar);
624 // we have to reset the selection, because the
625 // geometry could have changed
626 setCursor(bview, selection.start.par(), selection.start.pos());
627 selection.cursor = cursor;
628 setCursor(bview, selection.end.par(), selection.end.pos());
629 updateCounters(bview, cursor.row());
632 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
636 // decrement depth over selection and
637 // make a total rebreak of those paragraphs
638 void LyXText::decDepth(BufferView * bview)
640 // if there is no selection just set the layout
641 // of the current paragraph
642 if (!selection.set()) {
643 selection.start = cursor; // dummy selection
644 selection.end = cursor;
646 Paragraph * endpar = selection.end.par()->next();
647 Paragraph * undoendpar = endpar;
649 if (endpar && endpar->getDepth()) {
650 while (endpar && endpar->getDepth()) {
651 endpar = endpar->next();
655 endpar = endpar->next(); // because of parindents etc.
658 setUndo(bview, Undo::EDIT,
659 selection.start.par(), undoendpar);
661 LyXCursor tmpcursor = cursor; // store the current cursor
663 // ok we have a selection. This is always between sel_start_cursor
664 // and sel_end cursor
665 cursor = selection.start;
668 if (cursor.par()->params().depth()) {
669 cursor.par()->params()
670 .depth(cursor.par()->params().depth() - 1);
672 if (cursor.par() == selection.end.par()) {
675 cursor.par(cursor.par()->next());
678 redoParagraphs(bview, selection.start, endpar);
680 // we have to reset the selection, because the
681 // geometry could have changed
682 setCursor(bview, selection.start.par(),
683 selection.start.pos());
684 selection.cursor = cursor;
685 setCursor(bview, selection.end.par(), selection.end.pos());
686 updateCounters(bview, cursor.row());
689 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
693 // set font over selection and make a total rebreak of those paragraphs
694 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
696 // if there is no selection just set the current_font
697 if (!selection.set()) {
698 // Determine basis font
700 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
702 layoutfont = getLabelFont(bview->buffer(),
705 layoutfont = getLayoutFont(bview->buffer(),
708 // Update current font
709 real_current_font.update(font, toggleall);
711 // Reduce to implicit settings
712 current_font = real_current_font;
713 current_font.reduce(layoutfont);
714 // And resolve it completely
715 real_current_font.realize(layoutfont,
716 bview->buffer()->params.language);
720 LyXCursor tmpcursor = cursor; // store the current cursor
722 // ok we have a selection. This is always between sel_start_cursor
723 // and sel_end cursor
725 setUndo(bview, Undo::EDIT,
726 selection.start.par(), selection.end.par()->next());
728 cursor = selection.start;
729 while (cursor.par() != selection.end.par() ||
730 (cursor.pos() < selection.end.pos()))
732 if (cursor.pos() < cursor.par()->size()) {
733 // an open footnote should behave
735 setCharFont(bview, cursor.par(), cursor.pos(),
737 cursor.pos(cursor.pos() + 1);
740 cursor.par(cursor.par()->next());
745 redoParagraphs(bview, selection.start, selection.end.par()->next());
747 // we have to reset the selection, because the
748 // geometry could have changed
749 setCursor(bview, selection.start.par(), selection.start.pos());
750 selection.cursor = cursor;
751 setCursor(bview, selection.end.par(), selection.end.pos());
754 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
755 tmpcursor.boundary());
759 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
761 Row * tmprow = cur.row();
762 int y = cur.y() - tmprow->baseline();
764 setHeightOfRow(bview, tmprow);
766 while (tmprow->previous()
767 && tmprow->previous()->par() == tmprow->par()) {
768 tmprow = tmprow->previous();
769 y -= tmprow->height();
770 setHeightOfRow(bview, tmprow);
773 // we can set the refreshing parameters now
774 status(bview, LyXText::NEED_MORE_REFRESH);
776 refresh_row = tmprow;
777 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
781 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
783 Row * tmprow = cur.row();
785 int y = cur.y() - tmprow->baseline();
786 setHeightOfRow(bview, tmprow);
788 while (tmprow->previous()
789 && tmprow->previous()->par() == tmprow->par()) {
790 tmprow = tmprow->previous();
791 y -= tmprow->height();
794 // we can set the refreshing parameters now
795 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
797 refresh_row = tmprow;
799 status(bview, LyXText::NEED_MORE_REFRESH);
800 setCursor(bview, cur.par(), cur.pos());
804 // deletes and inserts again all paragaphs between the cursor
805 // and the specified par
806 // This function is needed after SetLayout and SetFont etc.
807 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
808 Paragraph const * endpar) const
811 Paragraph * tmppar = 0;
812 Paragraph * first_phys_par = 0;
814 Row * tmprow = cur.row();
816 int y = cur.y() - tmprow->baseline();
818 if (!tmprow->previous()) {
819 // a trick/hack for UNDO
820 // Can somebody please tell me _why_ this solves
822 first_phys_par = firstParagraph();
824 first_phys_par = tmprow->par();
825 while (tmprow->previous()
826 && tmprow->previous()->par() == first_phys_par)
828 tmprow = tmprow->previous();
829 y -= tmprow->height();
833 // we can set the refreshing parameters now
834 status(bview, LyXText::NEED_MORE_REFRESH);
836 refresh_row = tmprow->previous(); /* the real refresh row will
837 be deleted, so I store
841 tmppar = tmprow->next()->par();
844 while (tmppar != endpar) {
845 removeRow(tmprow->next());
847 tmppar = tmprow->next()->par();
852 // remove the first one
853 tmprow2 = tmprow; /* this is because tmprow->previous()
855 tmprow = tmprow->previous();
858 tmppar = first_phys_par;
862 insertParagraph(bview, tmppar, tmprow);
866 while (tmprow->next()
867 && tmprow->next()->par() == tmppar) {
868 tmprow = tmprow->next();
870 tmppar = tmppar->next();
872 } while (tmppar && tmppar != endpar);
874 // this is because of layout changes
876 refresh_y -= refresh_row->height();
877 setHeightOfRow(bview, refresh_row);
879 refresh_row = firstrow;
881 setHeightOfRow(bview, refresh_row);
884 if (tmprow && tmprow->next())
885 setHeightOfRow(bview, tmprow->next());
889 bool LyXText::fullRebreak(BufferView * bview)
895 if (need_break_row) {
896 breakAgain(bview, need_break_row);
904 // important for the screen
907 /* the cursor set functions have a special mechanism. When they
908 * realize, that you left an empty paragraph, they will delete it.
909 * They also delete the corresponding row */
911 // need the selection cursor:
912 void LyXText::setSelection(BufferView * bview)
914 bool const lsel = selection.set();
916 if (!selection.set()) {
917 last_sel_cursor = selection.cursor;
918 selection.start = selection.cursor;
919 selection.end = selection.cursor;
924 // first the toggling area
925 if (cursor.y() < last_sel_cursor.y()
926 || (cursor.y() == last_sel_cursor.y()
927 && cursor.x() < last_sel_cursor.x())) {
928 toggle_end_cursor = last_sel_cursor;
929 toggle_cursor = cursor;
931 toggle_end_cursor = cursor;
932 toggle_cursor = last_sel_cursor;
935 last_sel_cursor = cursor;
937 // and now the whole selection
939 if (selection.cursor.par() == cursor.par())
940 if (selection.cursor.pos() < cursor.pos()) {
941 selection.end = cursor;
942 selection.start = selection.cursor;
944 selection.end = selection.cursor;
945 selection.start = cursor;
947 else if (selection.cursor.y() < cursor.y() ||
948 (selection.cursor.y() == cursor.y()
949 && selection.cursor.x() < cursor.x())) {
950 selection.end = cursor;
951 selection.start = selection.cursor;
954 selection.end = selection.cursor;
955 selection.start = cursor;
958 // a selection with no contents is not a selection
959 if (selection.start.par() == selection.end.par() &&
960 selection.start.pos() == selection.end.pos())
961 selection.set(false);
963 if (inset_owner && (selection.set() || lsel))
964 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
968 string const LyXText::selectionAsString(Buffer const * buffer) const
970 if (!selection.set()) return string();
973 // Special handling if the whole selection is within one paragraph
974 if (selection.start.par() == selection.end.par()) {
975 result += selection.start.par()->asString(buffer,
976 selection.start.pos(),
977 selection.end.pos());
981 // The selection spans more than one paragraph
983 // First paragraph in selection
984 result += selection.start.par()->asString(buffer,
985 selection.start.pos(),
986 selection.start.par()->size())
989 // The paragraphs in between (if any)
990 LyXCursor tmpcur(selection.start);
991 tmpcur.par(tmpcur.par()->next());
992 while (tmpcur.par() != selection.end.par()) {
993 result += tmpcur.par()->asString(buffer, 0,
994 tmpcur.par()->size()) +"\n\n";
995 tmpcur.par(tmpcur.par()->next());
998 // Last paragraph in selection
999 result += selection.end.par()->asString(buffer, 0,
1000 selection.end.pos());
1006 void LyXText::clearSelection() const
1008 selection.set(false);
1009 selection.mark(false);
1010 selection.end = selection.start = selection.cursor = cursor;
1014 void LyXText::cursorHome(BufferView * bview) const
1016 setCursor(bview, cursor.par(), cursor.row()->pos());
1020 void LyXText::cursorEnd(BufferView * bview) const
1022 if (!cursor.row()->next()
1023 || cursor.row()->next()->par() != cursor.row()->par()) {
1024 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1026 if (cursor.par()->size() &&
1027 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1028 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1029 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1031 setCursor(bview,cursor.par(),
1032 rowLast(cursor.row()) + 1);
1038 void LyXText::cursorTop(BufferView * bview) const
1040 while (cursor.par()->previous())
1041 cursor.par(cursor.par()->previous());
1042 setCursor(bview, cursor.par(), 0);
1046 void LyXText::cursorBottom(BufferView * bview) const
1048 while (cursor.par()->next())
1049 cursor.par(cursor.par()->next());
1050 setCursor(bview, cursor.par(), cursor.par()->size());
1054 void LyXText::toggleFree(BufferView * bview,
1055 LyXFont const & font, bool toggleall)
1057 // If the mask is completely neutral, tell user
1058 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1059 // Could only happen with user style
1060 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1064 // Try implicit word selection
1065 // If there is a change in the language the implicit word selection
1067 LyXCursor resetCursor = cursor;
1068 bool implicitSelection = (font.language() == ignore_language
1069 && font.number() == LyXFont::IGNORE)
1070 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1073 setFont(bview, font, toggleall);
1075 // Implicit selections are cleared afterwards
1076 //and cursor is set to the original position.
1077 if (implicitSelection) {
1079 cursor = resetCursor;
1080 setCursor(bview, cursor.par(), cursor.pos());
1081 selection.cursor = cursor;
1084 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1089 LyXText::getStringToIndex(BufferView * bview)
1093 // Try implicit word selection
1094 // If there is a change in the language the implicit word selection
1096 LyXCursor resetCursor = cursor;
1097 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1099 if (!selection.set()) {
1100 bview->owner()->message(_("Nothing to index!"));
1103 if (selection.start.par() != selection.end.par()) {
1104 bview->owner()->message(_("Cannot index more than one paragraph!"));
1108 idxstring = selectionAsString(bview->buffer());
1110 // Implicit selections are cleared afterwards
1111 //and cursor is set to the original position.
1112 if (implicitSelection) {
1114 cursor = resetCursor;
1115 setCursor(bview, cursor.par(), cursor.pos());
1116 selection.cursor = cursor;
1121 Paragraph::size_type
1122 LyXText::beginningOfMainBody(Buffer const * buf,
1123 Paragraph const * par) const
1125 if (textclasslist.Style(buf->params.textclass,
1126 par->getLayout()).labeltype != LABEL_MANUAL)
1129 return par->beginningOfMainBody();
1133 /* the DTP switches for paragraphs. LyX will store them in the
1134 * first physicla paragraph. When a paragraph is broken, the top settings
1135 * rest, the bottom settings are given to the new one. So I can make shure,
1136 * they do not duplicate themself and you cannnot make dirty things with
1139 void LyXText::setParagraph(BufferView * bview,
1140 bool line_top, bool line_bottom,
1141 bool pagebreak_top, bool pagebreak_bottom,
1142 VSpace const & space_top,
1143 VSpace const & space_bottom,
1145 string labelwidthstring,
1148 LyXCursor tmpcursor = cursor;
1149 if (!selection.set()) {
1150 selection.start = cursor;
1151 selection.end = cursor;
1154 // make sure that the depth behind the selection are restored, too
1155 Paragraph * endpar = selection.end.par()->next();
1156 Paragraph * undoendpar = endpar;
1158 if (endpar && endpar->getDepth()) {
1159 while (endpar && endpar->getDepth()) {
1160 endpar = endpar->next();
1161 undoendpar = endpar;
1165 // because of parindents etc.
1166 endpar = endpar->next();
1169 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1172 Paragraph * tmppar = selection.end.par();
1173 while (tmppar != selection.start.par()->previous()) {
1174 setCursor(bview, tmppar, 0);
1175 status(bview, LyXText::NEED_MORE_REFRESH);
1176 refresh_row = cursor.row();
1177 refresh_y = cursor.y() - cursor.row()->baseline();
1178 cursor.par()->params().lineTop(line_top);
1179 cursor.par()->params().lineBottom(line_bottom);
1180 cursor.par()->params().pagebreakTop(pagebreak_top);
1181 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1182 cursor.par()->params().spaceTop(space_top);
1183 cursor.par()->params().spaceBottom(space_bottom);
1184 // does the layout allow the new alignment?
1185 if (align == LYX_ALIGN_LAYOUT)
1186 align = textclasslist
1187 .Style(bview->buffer()->params.textclass,
1188 cursor.par()->getLayout()).align;
1189 if (align & textclasslist
1190 .Style(bview->buffer()->params.textclass,
1191 cursor.par()->getLayout()).alignpossible) {
1192 if (align == textclasslist
1193 .Style(bview->buffer()->params.textclass,
1194 cursor.par()->getLayout()).align)
1195 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1197 cursor.par()->params().align(align);
1199 cursor.par()->setLabelWidthString(labelwidthstring);
1200 cursor.par()->params().noindent(noindent);
1201 tmppar = cursor.par()->previous();
1204 redoParagraphs(bview, selection.start, endpar);
1207 setCursor(bview, selection.start.par(), selection.start.pos());
1208 selection.cursor = cursor;
1209 setCursor(bview, selection.end.par(), selection.end.pos());
1210 setSelection(bview);
1211 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1213 bview->updateInset(inset_owner, true);
1217 char loweralphaCounter(int n)
1219 if (n < 1 || n > 26)
1229 char alphaCounter(int n)
1231 if (n < 1 || n > 26)
1239 char hebrewCounter(int n)
1241 static const char hebrew[22] = {
1242 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1243 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1244 '÷', 'ø', 'ù', 'ú'
1246 if (n < 1 || n > 22)
1254 string const romanCounter(int n)
1256 static char const * roman[20] = {
1257 "i", "ii", "iii", "iv", "v",
1258 "vi", "vii", "viii", "ix", "x",
1259 "xi", "xii", "xiii", "xiv", "xv",
1260 "xvi", "xvii", "xviii", "xix", "xx"
1262 if (n < 1 || n > 20)
1271 // set the counter of a paragraph. This includes the labels
1272 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1274 LyXLayout const & layout =
1275 textclasslist.Style(buf->params.textclass,
1278 LyXTextClass const & textclass =
1279 textclasslist.TextClass(buf->params.textclass);
1281 // copy the prev-counters to this one,
1282 // unless this is the first paragraph
1283 if (par->previous()) {
1284 for (int i = 0; i < 10; ++i) {
1285 par->setCounter(i, par->previous()->getFirstCounter(i));
1287 par->params().appendix(par->previous()->params().appendix());
1288 if (!par->params().appendix() && par->params().startOfAppendix()) {
1289 par->params().appendix(true);
1290 for (int i = 0; i < 10; ++i) {
1291 par->setCounter(i, 0);
1294 par->enumdepth = par->previous()->enumdepth;
1295 par->itemdepth = par->previous()->itemdepth;
1297 for (int i = 0; i < 10; ++i) {
1298 par->setCounter(i, 0);
1300 par->params().appendix(par->params().startOfAppendix());
1305 /* Maybe we have to increment the enumeration depth.
1306 * BUT, enumeration in a footnote is considered in isolation from its
1307 * surrounding paragraph so don't increment if this is the
1308 * first line of the footnote
1309 * AND, bibliographies can't have their depth changed ie. they
1310 * are always of depth 0
1313 && par->previous()->getDepth() < par->getDepth()
1314 && textclasslist.Style(buf->params.textclass,
1315 par->previous()->getLayout()
1316 ).labeltype == LABEL_COUNTER_ENUMI
1317 && par->enumdepth < 3
1318 && layout.labeltype != LABEL_BIBLIO) {
1322 // Maybe we have to decrement the enumeration depth, see note above
1324 && par->previous()->getDepth() > par->getDepth()
1325 && layout.labeltype != LABEL_BIBLIO) {
1326 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1327 par->setCounter(6 + par->enumdepth,
1328 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1329 /* reset the counters.
1330 * A depth change is like a breaking layout
1332 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1333 par->setCounter(i, 0);
1336 if (!par->params().labelString().empty()) {
1337 par->params().labelString(string());
1340 if (layout.margintype == MARGIN_MANUAL) {
1341 if (par->params().labelWidthString().empty()) {
1342 par->setLabelWidthString(layout.labelstring());
1345 par->setLabelWidthString(string());
1348 // is it a layout that has an automatic label?
1349 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1351 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1352 if (i >= 0 && i<= buf->params.secnumdepth) {
1353 par->incCounter(i); // increment the counter
1355 // Is there a label? Useful for Chapter layout
1356 if (!par->params().appendix()) {
1357 if (!layout.labelstring().empty())
1358 par->params().labelString(layout.labelstring());
1360 par->params().labelString(string());
1362 if (!layout.labelstring_appendix().empty())
1363 par->params().labelString(layout.labelstring_appendix());
1365 par->params().labelString(string());
1370 if (!par->params().appendix()) {
1371 switch (2 * LABEL_COUNTER_CHAPTER -
1372 textclass.maxcounter() + i) {
1373 case LABEL_COUNTER_CHAPTER:
1374 s << par->getCounter(i);
1376 case LABEL_COUNTER_SECTION:
1377 s << par->getCounter(i - 1) << '.'
1378 << par->getCounter(i);
1380 case LABEL_COUNTER_SUBSECTION:
1381 s << par->getCounter(i - 2) << '.'
1382 << par->getCounter(i - 1) << '.'
1383 << par->getCounter(i);
1385 case LABEL_COUNTER_SUBSUBSECTION:
1386 s << par->getCounter(i - 3) << '.'
1387 << par->getCounter(i - 2) << '.'
1388 << par->getCounter(i - 1) << '.'
1389 << par->getCounter(i);
1392 case LABEL_COUNTER_PARAGRAPH:
1393 s << par->getCounter(i - 4) << '.'
1394 << par->getCounter(i - 3) << '.'
1395 << par->getCounter(i - 2) << '.'
1396 << par->getCounter(i - 1) << '.'
1397 << par->getCounter(i);
1399 case LABEL_COUNTER_SUBPARAGRAPH:
1400 s << par->getCounter(i - 5) << '.'
1401 << par->getCounter(i - 4) << '.'
1402 << par->getCounter(i - 3) << '.'
1403 << par->getCounter(i - 2) << '.'
1404 << par->getCounter(i - 1) << '.'
1405 << par->getCounter(i);
1409 // Can this ever be reached? And in the
1410 // case it is, how can this be correct?
1412 s << par->getCounter(i) << '.';
1415 } else { // appendix
1416 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1417 case LABEL_COUNTER_CHAPTER:
1418 if (par->isRightToLeftPar(buf->params))
1419 s << hebrewCounter(par->getCounter(i));
1421 s << alphaCounter(par->getCounter(i));
1423 case LABEL_COUNTER_SECTION:
1424 if (par->isRightToLeftPar(buf->params))
1425 s << hebrewCounter(par->getCounter(i - 1));
1427 s << alphaCounter(par->getCounter(i - 1));
1430 << par->getCounter(i);
1433 case LABEL_COUNTER_SUBSECTION:
1434 if (par->isRightToLeftPar(buf->params))
1435 s << hebrewCounter(par->getCounter(i - 2));
1437 s << alphaCounter(par->getCounter(i - 2));
1440 << par->getCounter(i-1) << '.'
1441 << par->getCounter(i);
1444 case LABEL_COUNTER_SUBSUBSECTION:
1445 if (par->isRightToLeftPar(buf->params))
1446 s << hebrewCounter(par->getCounter(i-3));
1448 s << alphaCounter(par->getCounter(i-3));
1451 << par->getCounter(i-2) << '.'
1452 << par->getCounter(i-1) << '.'
1453 << par->getCounter(i);
1456 case LABEL_COUNTER_PARAGRAPH:
1457 if (par->isRightToLeftPar(buf->params))
1458 s << hebrewCounter(par->getCounter(i-4));
1460 s << alphaCounter(par->getCounter(i-4));
1463 << par->getCounter(i-3) << '.'
1464 << par->getCounter(i-2) << '.'
1465 << par->getCounter(i-1) << '.'
1466 << par->getCounter(i);
1469 case LABEL_COUNTER_SUBPARAGRAPH:
1470 if (par->isRightToLeftPar(buf->params))
1471 s << hebrewCounter(par->getCounter(i-5));
1473 s << alphaCounter(par->getCounter(i-5));
1476 << par->getCounter(i-4) << '.'
1477 << par->getCounter(i-3) << '.'
1478 << par->getCounter(i-2) << '.'
1479 << par->getCounter(i-1) << '.'
1480 << par->getCounter(i);
1484 // Can this ever be reached? And in the
1485 // case it is, how can this be correct?
1487 s << par->getCounter(i) << '.';
1493 par->params().labelString(par->params().labelString() +s.str().c_str());
1494 // We really want to remove the c_str as soon as
1497 for (i++; i < 10; ++i) {
1498 // reset the following counters
1499 par->setCounter(i, 0);
1501 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1502 for (i++; i < 10; ++i) {
1503 // reset the following counters
1504 par->setCounter(i, 0);
1506 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1507 par->incCounter(i + par->enumdepth);
1508 int number = par->getCounter(i + par->enumdepth);
1512 switch (par->enumdepth) {
1514 if (par->isRightToLeftPar(buf->params))
1516 << hebrewCounter(number)
1520 << loweralphaCounter(number)
1524 if (par->isRightToLeftPar(buf->params))
1525 s << '.' << romanCounter(number);
1527 s << romanCounter(number) << '.';
1530 if (par->isRightToLeftPar(buf->params))
1532 << alphaCounter(number);
1534 s << alphaCounter(number)
1538 if (par->isRightToLeftPar(buf->params))
1545 par->params().labelString(s.str().c_str());
1547 for (i += par->enumdepth + 1; i < 10; ++i) {
1548 // reset the following counters
1549 par->setCounter(i, 0);
1553 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1554 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1556 int number = par->getCounter(i);
1558 InsetCommandParams p( "bibitem" );
1559 par->bibkey = new InsetBibKey(p);
1561 par->bibkey->setCounter(number);
1562 par->params().labelString(layout.labelstring());
1564 // In biblio should't be following counters but...
1566 string s = layout.labelstring();
1568 // the caption hack:
1569 if (layout.labeltype == LABEL_SENSITIVE) {
1570 bool isOK (par->inInset() && par->inInset()->owner() &&
1571 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1574 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1576 = floatList.getType(tmp->type());
1577 // We should get the correct number here too.
1578 s = fl.name() + " #:";
1580 /* par->SetLayout(0);
1581 s = layout->labelstring; */
1582 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1583 ? " :úåòîùî øñç" : "Senseless: ";
1586 par->params().labelString(s);
1588 /* reset the enumeration counter. They are always resetted
1589 * when there is any other layout between */
1590 for (int i = 6 + par->enumdepth; i < 10; ++i)
1591 par->setCounter(i, 0);
1596 // Updates all counters BEHIND the row. Changed paragraphs
1597 // with a dynamic left margin will be rebroken.
1598 void LyXText::updateCounters(BufferView * bview, Row * row) const
1606 par = row->par()->next();
1610 while (row->par() != par)
1613 setCounter(bview->buffer(), par);
1615 // now check for the headline layouts. remember that they
1616 // have a dynamic left margin
1617 if ((textclasslist.Style(bview->buffer()->params.textclass,
1618 par->layout).margintype == MARGIN_DYNAMIC
1619 || textclasslist.Style(bview->buffer()->params.textclass,
1620 par->layout).labeltype == LABEL_SENSITIVE)) {
1622 // Rebreak the paragraph
1623 removeParagraph(row);
1624 appendParagraph(bview, row);
1631 void LyXText::insertInset(BufferView * bview, Inset * inset)
1633 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1635 setUndo(bview, Undo::INSERT,
1636 cursor.par(), cursor.par()->next());
1637 cursor.par()->insertInset(cursor.pos(), inset);
1638 // Just to rebreak and refresh correctly.
1639 // The character will not be inserted a second time
1640 insertChar(bview, Paragraph::META_INSET);
1642 // If we enter a highly editable inset the cursor should be to before
1643 // the inset. This couldn't happen before as Undo was not handled inside
1644 // inset now after the Undo LyX tries to call inset->Edit(...) again
1645 // and cannot do this as the cursor is behind the inset and GetInset
1646 // does not return the inset!
1647 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1648 cursorLeft(bview, true);
1654 void LyXText::copyEnvironmentType()
1656 copylayouttype = cursor.par()->getLayout();
1660 void LyXText::pasteEnvironmentType(BufferView * bview)
1662 setLayout(bview, copylayouttype);
1666 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1668 // Stuff what we got on the clipboard. Even if there is no selection.
1670 // There is a problem with having the stuffing here in that the
1671 // larger the selection the slower LyX will get. This can be
1672 // solved by running the line below only when the selection has
1673 // finished. The solution used currently just works, to make it
1674 // faster we need to be more clever and probably also have more
1675 // calls to stuffClipboard. (Lgb)
1676 bview->stuffClipboard(selectionAsString(bview->buffer()));
1678 // This doesn't make sense, if there is no selection
1679 if (!selection.set())
1682 // OK, we have a selection. This is always between selection.start
1683 // and selection.end
1685 // make sure that the depth behind the selection are restored, too
1686 Paragraph * endpar = selection.end.par()->next();
1687 Paragraph * undoendpar = endpar;
1689 if (endpar && endpar->getDepth()) {
1690 while (endpar && endpar->getDepth()) {
1691 endpar = endpar->next();
1692 undoendpar = endpar;
1694 } else if (endpar) {
1695 endpar = endpar->next(); // because of parindents etc.
1698 setUndo(bview, Undo::DELETE,
1699 selection.start.par(), undoendpar);
1701 // there are two cases: cut only within one paragraph or
1702 // more than one paragraph
1703 if (selection.start.par() == selection.end.par()) {
1704 // only within one paragraph
1705 endpar = selection.end.par();
1706 int pos = selection.end.pos();
1707 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1708 selection.start.pos(), pos,
1709 bview->buffer()->params.textclass, doclear,
1711 selection.end.pos(pos);
1713 endpar = selection.end.par();
1714 int pos = selection.end.pos();
1715 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1716 selection.start.pos(), pos,
1717 bview->buffer()->params.textclass, doclear,
1720 selection.end.par(endpar);
1721 selection.end.pos(pos);
1722 cursor.pos(selection.end.pos());
1724 endpar = endpar->next();
1726 // sometimes necessary
1728 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1730 redoParagraphs(bview, selection.start, endpar);
1732 // cutSelection can invalidate the cursor so we need to set
1734 cursor = selection.start;
1736 // need a valid cursor. (Lgb)
1739 setCursor(bview, cursor.par(), cursor.pos());
1740 selection.cursor = cursor;
1741 updateCounters(bview, cursor.row());
1745 void LyXText::copySelection(BufferView * bview)
1747 // Stuff what we got on the clipboard. Even if there is no selection.
1749 // There is a problem with having the stuffing here in that the
1750 // larger the selection the slower LyX will get. This can be
1751 // solved by running the line below only when the selection has
1752 // finished. The solution used currently just works, to make it
1753 // faster we need to be more clever and probably also have more
1754 // calls to stuffClipboard. (Lgb)
1755 bview->stuffClipboard(selectionAsString(bview->buffer()));
1757 // this doesnt make sense, if there is no selection
1758 if (!selection.set())
1761 // ok we have a selection. This is always between selection.start
1762 // and sel_end cursor
1764 // copy behind a space if there is one
1765 while (selection.start.par()->size() > selection.start.pos()
1766 && selection.start.par()->isLineSeparator(selection.start.pos())
1767 && (selection.start.par() != selection.end.par()
1768 || selection.start.pos() < selection.end.pos()))
1769 selection.start.pos(selection.start.pos() + 1);
1771 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1772 selection.start.pos(), selection.end.pos(),
1773 bview->buffer()->params.textclass);
1777 void LyXText::pasteSelection(BufferView * bview)
1779 // this does not make sense, if there is nothing to paste
1780 if (!CutAndPaste::checkPastePossible(cursor.par()))
1783 setUndo(bview, Undo::INSERT,
1784 cursor.par(), cursor.par()->next());
1787 Paragraph * actpar = cursor.par();
1789 int pos = cursor.pos();
1790 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1791 bview->buffer()->params.textclass);
1793 redoParagraphs(bview, cursor, endpar);
1795 setCursor(bview, cursor.par(), cursor.pos());
1798 selection.cursor = cursor;
1799 setCursor(bview, actpar, pos);
1800 setSelection(bview);
1801 updateCounters(bview, cursor.row());
1805 // returns a pointer to the very first Paragraph
1806 Paragraph * LyXText::firstParagraph() const
1808 return ownerParagraph();
1812 // sets the selection over the number of characters of string, no check!!
1813 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1818 selection.cursor = cursor;
1819 for (string::size_type i = 0; i < str.length(); ++i)
1821 setSelection(bview);
1825 // simple replacing. The font of the first selected character is used
1826 void LyXText::replaceSelectionWithString(BufferView * bview,
1829 setCursorParUndo(bview);
1832 if (!selection.set()) { // create a dummy selection
1833 selection.end = cursor;
1834 selection.start = cursor;
1837 // Get font setting before we cut
1838 Paragraph::size_type pos = selection.end.pos();
1839 LyXFont const font = selection.start.par()
1840 ->getFontSettings(bview->buffer()->params,
1841 selection.start.pos());
1843 // Insert the new string
1844 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1845 selection.end.par()->insertChar(pos, (*cit), font);
1849 // Cut the selection
1850 cutSelection(bview, true, false);
1856 // needed to insert the selection
1857 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1859 Paragraph * par = cursor.par();
1860 Paragraph::size_type pos = cursor.pos();
1861 Paragraph * endpar = cursor.par()->next();
1863 setCursorParUndo(bview);
1865 // only to be sure, should not be neccessary
1868 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1870 redoParagraphs(bview, cursor, endpar);
1871 setCursor(bview, cursor.par(), cursor.pos());
1872 selection.cursor = cursor;
1873 setCursor(bview, par, pos);
1874 setSelection(bview);
1878 // turns double-CR to single CR, others where converted into one
1879 // blank. Then InsertStringAsLines is called
1880 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1882 string linestr(str);
1883 bool newline_inserted = false;
1884 for (string::size_type i = 0; i < linestr.length(); ++i) {
1885 if (linestr[i] == '\n') {
1886 if (newline_inserted) {
1887 // we know that \r will be ignored by
1888 // InsertStringA. Of course, it is a dirty
1889 // trick, but it works...
1890 linestr[i - 1] = '\r';
1894 newline_inserted = true;
1896 } else if (IsPrintable(linestr[i])) {
1897 newline_inserted = false;
1900 insertStringAsLines(bview, linestr);
1904 bool LyXText::gotoNextInset(BufferView * bview,
1905 std::vector<Inset::Code> const & codes,
1906 string const & contents) const
1908 LyXCursor res = cursor;
1911 if (res.pos() < res.par()->size() - 1) {
1912 res.pos(res.pos() + 1);
1914 res.par(res.par()->next());
1918 } while (res.par() &&
1919 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1920 && (inset = res.par()->getInset(res.pos())) != 0
1921 && find(codes.begin(), codes.end(), inset->lyxCode())
1923 && (contents.empty() ||
1924 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1928 setCursor(bview, res.par(), res.pos());
1935 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1936 Paragraph::size_type pos)
1938 LyXCursor tmpcursor;
1941 Paragraph::size_type z;
1942 Row * row = getRow(par, pos, y);
1944 // is there a break one row above
1945 if (row->previous() && row->previous()->par() == row->par()) {
1946 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1947 if (z >= row->pos()) {
1948 // set the dimensions of the row above
1949 y -= row->previous()->height();
1951 refresh_row = row->previous();
1952 status(bview, LyXText::NEED_MORE_REFRESH);
1954 breakAgain(bview, row->previous());
1956 // set the cursor again. Otherwise
1957 // dangling pointers are possible
1958 setCursor(bview, cursor.par(), cursor.pos(),
1959 false, cursor.boundary());
1960 selection.cursor = cursor;
1965 int const tmpheight = row->height();
1966 Paragraph::size_type const tmplast = rowLast(row);
1970 breakAgain(bview, row);
1971 if (row->height() == tmpheight && rowLast(row) == tmplast)
1972 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1974 status(bview, LyXText::NEED_MORE_REFRESH);
1976 // check the special right address boxes
1977 if (textclasslist.Style(bview->buffer()->params.textclass,
1978 par->getLayout()).margintype
1979 == MARGIN_RIGHT_ADDRESS_BOX) {
1986 redoDrawingOfParagraph(bview, tmpcursor);
1989 // set the cursor again. Otherwise dangling pointers are possible
1990 // also set the selection
1992 if (selection.set()) {
1994 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1995 false, selection.cursor.boundary());
1996 selection.cursor = cursor;
1997 setCursorIntern(bview, selection.start.par(),
1998 selection.start.pos(),
1999 false, selection.start.boundary());
2000 selection.start = cursor;
2001 setCursorIntern(bview, selection.end.par(),
2002 selection.end.pos(),
2003 false, selection.end.boundary());
2004 selection.end = cursor;
2005 setCursorIntern(bview, last_sel_cursor.par(),
2006 last_sel_cursor.pos(),
2007 false, last_sel_cursor.boundary());
2008 last_sel_cursor = cursor;
2011 setCursorIntern(bview, cursor.par(), cursor.pos(),
2012 false, cursor.boundary());
2016 // returns false if inset wasn't found
2017 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2019 // first check the current paragraph
2020 int pos = cursor.par()->getPositionOfInset(inset);
2022 checkParagraph(bview, cursor.par(), pos);
2026 // check every paragraph
2028 Paragraph * par = firstParagraph();
2030 pos = par->getPositionOfInset(inset);
2032 checkParagraph(bview, par, pos);
2042 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2043 Paragraph::size_type pos,
2044 bool setfont, bool boundary) const
2046 LyXCursor old_cursor = cursor;
2047 setCursorIntern(bview, par, pos, setfont, boundary);
2048 deleteEmptyParagraphMechanism(bview, old_cursor);
2052 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2053 Paragraph::size_type pos, bool boundary) const
2057 cur.boundary(boundary);
2059 // get the cursor y position in text
2061 Row * row = getRow(par, pos, y);
2062 // y is now the beginning of the cursor row
2063 y += row->baseline();
2064 // y is now the cursor baseline
2067 // now get the cursors x position
2069 float fill_separator;
2071 float fill_label_hfill;
2072 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2074 Paragraph::size_type cursor_vpos = 0;
2075 Paragraph::size_type last = rowLastPrintable(row);
2077 if (pos > last + 1) // This shouldn't happen.
2079 else if (pos < row->pos())
2082 if (last < row->pos())
2083 cursor_vpos = row->pos();
2084 else if (pos > last && !boundary)
2085 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2086 ? row->pos() : last + 1;
2087 else if (pos > row->pos() &&
2088 (pos > last || boundary))
2089 /// Place cursor after char at (logical) position pos - 1
2090 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2091 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2093 /// Place cursor before char at (logical) position pos
2094 cursor_vpos = (bidi_level(pos) % 2 == 0)
2095 ? log2vis(pos) : log2vis(pos) + 1;
2097 Paragraph::size_type main_body =
2098 beginningOfMainBody(bview->buffer(), row->par());
2099 if ((main_body > 0) &&
2100 ((main_body-1 > last) ||
2101 !row->par()->isLineSeparator(main_body-1)))
2104 for (Paragraph::size_type vpos = row->pos();
2105 vpos < cursor_vpos; ++vpos) {
2106 pos = vis2log(vpos);
2107 if (main_body > 0 && pos == main_body - 1) {
2108 x += fill_label_hfill +
2109 lyxfont::width(textclasslist.Style(
2110 bview->buffer()->params.textclass,
2111 row->par()->getLayout())
2113 getLabelFont(bview->buffer(), row->par()));
2114 if (row->par()->isLineSeparator(main_body-1))
2115 x -= singleWidth(bview, row->par(),main_body-1);
2117 if (hfillExpansion(bview->buffer(), row, pos)) {
2118 x += singleWidth(bview, row->par(), pos);
2119 if (pos >= main_body)
2122 x += fill_label_hfill;
2123 } else if (row->par()->isSeparator(pos)) {
2124 x += singleWidth(bview, row->par(), pos);
2125 if (pos >= main_body)
2126 x += fill_separator;
2128 x += singleWidth(bview, row->par(), pos);
2137 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2138 Paragraph::size_type pos,
2139 bool setfont, bool boundary) const
2141 InsetText * it = static_cast<InsetText *>(par->inInset());
2143 if (it != inset_owner) {
2144 lyxerr << "InsetText is " << it << endl;
2145 lyxerr << "inset_owner is " << inset_owner << endl;
2146 #warning I belive this code is wrong. (Lgb)
2147 #warning Jürgen, have a look at this. (Lgb)
2148 #warning Hmmm, I guess you are right but we
2149 #warning should verify when this is needed
2150 // Jürgen, would you like to have a look?
2151 // I guess we need to move the outer cursor
2152 // and open and lock the inset (bla bla bla)
2153 // stuff I don't know... so can you have a look?
2155 // I moved the lyxerr stuff in here so we can see if this
2156 // is actually really needed and where!
2158 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2164 setCursor(bview, cursor, par, pos, boundary);
2166 setCurrentFont(bview);
2170 void LyXText::setCurrentFont(BufferView * bview) const
2172 Paragraph::size_type pos = cursor.pos();
2173 if (cursor.boundary() && pos > 0)
2177 if (pos == cursor.par()->size())
2179 else // potentional bug... BUG (Lgb)
2180 if (cursor.par()->isSeparator(pos)) {
2181 if (pos > cursor.row()->pos() &&
2182 bidi_level(pos) % 2 ==
2183 bidi_level(pos - 1) % 2)
2185 else if (pos + 1 < cursor.par()->size())
2191 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2192 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2194 if (cursor.pos() == cursor.par()->size() &&
2195 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2196 !cursor.boundary()) {
2197 Language const * lang =
2198 cursor.par()->getParLanguage(bview->buffer()->params);
2199 current_font.setLanguage(lang);
2200 current_font.setNumber(LyXFont::OFF);
2201 real_current_font.setLanguage(lang);
2202 real_current_font.setNumber(LyXFont::OFF);
2207 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2209 LyXCursor old_cursor = cursor;
2211 setCursorFromCoordinates(bview, cursor, x, y);
2212 setCurrentFont(bview);
2213 deleteEmptyParagraphMechanism(bview, old_cursor);
2217 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2220 // Get the row first.
2222 Row * row = getRowNearY(y);
2224 int const column = getColumnNearX(bview, row, x, bound);
2226 cur.par(row->par());
2227 cur.pos(row->pos() + column);
2229 cur.y(y + row->baseline());
2231 cur.boundary(bound);
2235 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2237 if (cursor.pos() > 0) {
2238 bool boundary = cursor.boundary();
2239 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2240 if (!internal && !boundary &&
2241 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2242 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2243 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2244 Paragraph * par = cursor.par()->previous();
2245 setCursor(bview, par, par->size());
2250 void LyXText::cursorRight(BufferView * bview, bool internal) const
2252 if (!internal && cursor.boundary() &&
2253 !cursor.par()->isNewline(cursor.pos()))
2254 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2255 else if (cursor.pos() < cursor.par()->size()) {
2256 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2258 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2259 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2260 } else if (cursor.par()->next())
2261 setCursor(bview, cursor.par()->next(), 0);
2265 void LyXText::cursorUp(BufferView * bview) const
2267 setCursorFromCoordinates(bview, cursor.x_fix(),
2268 cursor.y() - cursor.row()->baseline() - 1);
2272 void LyXText::cursorDown(BufferView * bview) const
2274 setCursorFromCoordinates(bview, cursor.x_fix(),
2275 cursor.y() - cursor.row()->baseline()
2276 + cursor.row()->height() + 1);
2280 void LyXText::cursorUpParagraph(BufferView * bview) const
2282 if (cursor.pos() > 0) {
2283 setCursor(bview, cursor.par(), 0);
2285 else if (cursor.par()->previous()) {
2286 setCursor(bview, cursor.par()->previous(), 0);
2291 void LyXText::cursorDownParagraph(BufferView * bview) const
2293 if (cursor.par()->next()) {
2294 setCursor(bview, cursor.par()->next(), 0);
2296 setCursor(bview, cursor.par(), cursor.par()->size());
2301 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2302 LyXCursor const & old_cursor) const
2304 // Would be wrong to delete anything if we have a selection.
2305 if (selection.set()) return;
2307 // We allow all kinds of "mumbo-jumbo" when freespacing.
2308 if (textclasslist.Style(bview->buffer()->params.textclass,
2309 old_cursor.par()->getLayout()).free_spacing)
2312 bool deleted = false;
2314 /* Ok I'll put some comments here about what is missing.
2315 I have fixed BackSpace (and thus Delete) to not delete
2316 double-spaces automagically. I have also changed Cut,
2317 Copy and Paste to hopefully do some sensible things.
2318 There are still some small problems that can lead to
2319 double spaces stored in the document file or space at
2320 the beginning of paragraphs. This happens if you have
2321 the cursor betwenn to spaces and then save. Or if you
2322 cut and paste and the selection have a space at the
2323 beginning and then save right after the paste. I am
2324 sure none of these are very hard to fix, but I will
2325 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2326 that I can get some feedback. (Lgb)
2329 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2330 // delete the LineSeparator.
2333 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2334 // delete the LineSeparator.
2337 // If the pos around the old_cursor were spaces, delete one of them.
2338 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2339 // Only if the cursor has really moved
2341 if (old_cursor.pos() > 0
2342 && old_cursor.pos() < old_cursor.par()->size()
2343 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2344 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2345 old_cursor.par()->erase(old_cursor.pos() - 1);
2346 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2348 if (old_cursor.par() == cursor.par() &&
2349 cursor.pos() > old_cursor.pos()) {
2350 setCursorIntern(bview, cursor.par(),
2353 setCursorIntern(bview, cursor.par(),
2359 // Do not delete empty paragraphs with keepempty set.
2360 if ((textclasslist.Style(bview->buffer()->params.textclass,
2361 old_cursor.par()->getLayout())).keepempty)
2364 LyXCursor tmpcursor;
2366 if (old_cursor.par() != cursor.par()) {
2367 if ((old_cursor.par()->size() == 0
2368 || (old_cursor.par()->size() == 1
2369 && old_cursor.par()->isLineSeparator(0)))) {
2370 // ok, we will delete anything
2372 // make sure that you do not delete any environments
2373 status(bview, LyXText::NEED_MORE_REFRESH);
2376 if (old_cursor.row()->previous()) {
2377 refresh_row = old_cursor.row()->previous();
2378 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2380 cursor = old_cursor; // that undo can restore the right cursor position
2381 Paragraph * endpar = old_cursor.par()->next();
2382 if (endpar && endpar->getDepth()) {
2383 while (endpar && endpar->getDepth()) {
2384 endpar = endpar->next();
2387 setUndo(bview, Undo::DELETE,
2393 removeRow(old_cursor.row());
2394 if (ownerParagraph() == old_cursor.par()) {
2395 ownerParagraph(ownerParagraph()->next());
2398 delete old_cursor.par();
2400 /* Breakagain the next par. Needed
2401 * because of the parindent that
2402 * can occur or dissappear. The
2403 * next row can change its height,
2404 * if there is another layout before */
2405 if (refresh_row->next()) {
2406 breakAgain(bview, refresh_row->next());
2407 updateCounters(bview, refresh_row);
2409 setHeightOfRow(bview, refresh_row);
2411 refresh_row = old_cursor.row()->next();
2412 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2415 cursor = old_cursor; // that undo can restore the right cursor position
2416 Paragraph * endpar = old_cursor.par()->next();
2417 if (endpar && endpar->getDepth()) {
2418 while (endpar && endpar->getDepth()) {
2419 endpar = endpar->next();
2422 setUndo(bview, Undo::DELETE,
2428 removeRow(old_cursor.row());
2430 if (ownerParagraph() == old_cursor.par()) {
2431 ownerParagraph(ownerParagraph()->next());
2434 delete old_cursor.par();
2436 /* Breakagain the next par. Needed
2437 because of the parindent that can
2438 occur or dissappear.
2439 The next row can change its height,
2440 if there is another layout before
2443 breakAgain(bview, refresh_row);
2444 updateCounters(bview, refresh_row->previous());
2450 setCursorIntern(bview, cursor.par(), cursor.pos());
2452 if (selection.cursor.par() == old_cursor.par()
2453 && selection.cursor.pos() == selection.cursor.pos()) {
2454 // correct selection
2455 selection.cursor = cursor;
2459 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2460 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2462 setCursorIntern(bview, cursor.par(), cursor.pos());
2463 selection.cursor = cursor;
2470 void LyXText::toggleAppendix(BufferView * bview)
2472 Paragraph * par = cursor.par();
2473 bool start = !par->params().startOfAppendix();
2475 // ensure that we have only one start_of_appendix in this document
2476 Paragraph * tmp = firstParagraph();
2477 for (; tmp; tmp = tmp->next()) {
2478 tmp->params().startOfAppendix(false);
2481 par->params().startOfAppendix(start);
2483 // we can set the refreshing parameters now
2484 status(bview, LyXText::NEED_MORE_REFRESH);
2486 refresh_row = 0; // not needed for full update
2487 updateCounters(bview, 0);
2488 setCursor(bview, cursor.par(), cursor.pos());
2492 Paragraph * LyXText::ownerParagraph() const
2495 return inset_owner->paragraph();
2497 return bv_owner->buffer()->paragraph;
2501 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2504 inset_owner->paragraph(p);
2506 bv_owner->buffer()->paragraph = p;
2511 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2513 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2514 if (op && op->inInset()) {
2515 static_cast<InsetText *>(op->inInset())->paragraph(p);
2518 inset_owner->paragraph(p);
2520 bv_owner->buffer()->paragraph = p;
2527 LyXText::text_status LyXText::status() const
2533 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2535 // well as much as I know && binds more then || so the above and the
2536 // below are identical (this for your known use of parentesis!)
2537 // Now some explanation:
2538 // We should only go up with refreshing code so this means that if
2539 // we have a MORE refresh we should never set it to LITTLE if we still
2540 // didn't handle it (and then it will be UNCHANGED. Now as long as
2541 // we stay inside one LyXText this may work but we need to tell the
2542 // outermost LyXText that it should REALLY draw us if there is some
2543 // change in a Inset::LyXText. So you see that when we are inside a
2544 // inset's LyXText we give the LITTLE to the outermost LyXText to
2545 // tell'em that it should redraw the actual row (where the inset
2546 // resides! Capito?!
2548 if ((status_ != NEED_MORE_REFRESH)
2549 || (status_ == NEED_MORE_REFRESH
2550 && st != NEED_VERY_LITTLE_REFRESH))
2553 if (inset_owner && st != UNCHANGED) {
2554 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2555 if (!bview->text->refresh_row) {
2556 bview->text->refresh_row = bview->text->cursor.row();
2557 bview->text->refresh_y = bview->text->cursor.y() -
2558 bview->text->cursor.row()->baseline();