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 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
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)
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);
1710 selection.end.pos(pos);
1712 endpar = selection.end.par();
1713 int pos = selection.end.pos();
1714 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1715 selection.start.pos(), pos,
1716 bview->buffer()->params.textclass, doclear);
1718 selection.end.par(endpar);
1719 selection.end.pos(pos);
1720 cursor.pos(selection.end.pos());
1722 endpar = endpar->next();
1724 // sometimes necessary
1726 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1728 redoParagraphs(bview, selection.start, endpar);
1730 // cutSelection can invalidate the cursor so we need to set
1732 cursor = selection.start;
1734 // need a valid cursor. (Lgb)
1737 setCursor(bview, cursor.par(), cursor.pos());
1738 selection.cursor = cursor;
1739 updateCounters(bview, cursor.row());
1743 void LyXText::copySelection(BufferView * bview)
1745 // Stuff what we got on the clipboard. Even if there is no selection.
1747 // There is a problem with having the stuffing here in that the
1748 // larger the selection the slower LyX will get. This can be
1749 // solved by running the line below only when the selection has
1750 // finished. The solution used currently just works, to make it
1751 // faster we need to be more clever and probably also have more
1752 // calls to stuffClipboard. (Lgb)
1753 bview->stuffClipboard(selectionAsString(bview->buffer()));
1755 // this doesnt make sense, if there is no selection
1756 if (!selection.set())
1759 // ok we have a selection. This is always between selection.start
1760 // and sel_end cursor
1762 // copy behind a space if there is one
1763 while (selection.start.par()->size() > selection.start.pos()
1764 && selection.start.par()->isLineSeparator(selection.start.pos())
1765 && (selection.start.par() != selection.end.par()
1766 || selection.start.pos() < selection.end.pos()))
1767 selection.start.pos(selection.start.pos() + 1);
1769 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1770 selection.start.pos(), selection.end.pos(),
1771 bview->buffer()->params.textclass);
1775 void LyXText::pasteSelection(BufferView * bview)
1777 // this does not make sense, if there is nothing to paste
1778 if (!CutAndPaste::checkPastePossible(cursor.par()))
1781 setUndo(bview, Undo::INSERT,
1782 cursor.par(), cursor.par()->next());
1785 Paragraph * actpar = cursor.par();
1787 int pos = cursor.pos();
1788 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1789 bview->buffer()->params.textclass);
1791 redoParagraphs(bview, cursor, endpar);
1793 setCursor(bview, cursor.par(), cursor.pos());
1796 selection.cursor = cursor;
1797 setCursor(bview, actpar, pos);
1798 setSelection(bview);
1799 updateCounters(bview, cursor.row());
1803 // returns a pointer to the very first Paragraph
1804 Paragraph * LyXText::firstParagraph() const
1806 return ownerParagraph();
1810 // sets the selection over the number of characters of string, no check!!
1811 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1816 selection.cursor = cursor;
1817 for (string::size_type i = 0; i < str.length(); ++i)
1819 setSelection(bview);
1823 // simple replacing. The font of the first selected character is used
1824 void LyXText::replaceSelectionWithString(BufferView * bview,
1827 setCursorParUndo(bview);
1830 if (!selection.set()) { // create a dummy selection
1831 selection.end = cursor;
1832 selection.start = cursor;
1835 // Get font setting before we cut
1836 Paragraph::size_type pos = selection.end.pos();
1837 LyXFont const font = selection.start.par()
1838 ->getFontSettings(bview->buffer()->params,
1839 selection.start.pos());
1841 // Insert the new string
1842 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1843 selection.end.par()->insertChar(pos, (*cit), font);
1847 // Cut the selection
1848 cutSelection(bview);
1854 // needed to insert the selection
1855 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1857 Paragraph * par = cursor.par();
1858 Paragraph::size_type pos = cursor.pos();
1859 Paragraph * endpar = cursor.par()->next();
1861 setCursorParUndo(bview);
1863 // only to be sure, should not be neccessary
1866 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1868 redoParagraphs(bview, cursor, endpar);
1869 setCursor(bview, cursor.par(), cursor.pos());
1870 selection.cursor = cursor;
1871 setCursor(bview, par, pos);
1872 setSelection(bview);
1876 // turns double-CR to single CR, others where converted into one
1877 // blank. Then InsertStringAsLines is called
1878 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1880 string linestr(str);
1881 bool newline_inserted = false;
1882 for (string::size_type i = 0; i < linestr.length(); ++i) {
1883 if (linestr[i] == '\n') {
1884 if (newline_inserted) {
1885 // we know that \r will be ignored by
1886 // InsertStringA. Of course, it is a dirty
1887 // trick, but it works...
1888 linestr[i - 1] = '\r';
1892 newline_inserted = true;
1894 } else if (IsPrintable(linestr[i])) {
1895 newline_inserted = false;
1898 insertStringAsLines(bview, linestr);
1902 bool LyXText::gotoNextInset(BufferView * bview,
1903 std::vector<Inset::Code> const & codes,
1904 string const & contents) const
1906 LyXCursor res = cursor;
1909 if (res.pos() < res.par()->size() - 1) {
1910 res.pos(res.pos() + 1);
1912 res.par(res.par()->next());
1916 } while (res.par() &&
1917 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1918 && (inset = res.par()->getInset(res.pos())) != 0
1919 && find(codes.begin(), codes.end(), inset->lyxCode())
1921 && (contents.empty() ||
1922 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1926 setCursor(bview, res.par(), res.pos());
1933 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1934 Paragraph::size_type pos)
1936 LyXCursor tmpcursor;
1939 Paragraph::size_type z;
1940 Row * row = getRow(par, pos, y);
1942 // is there a break one row above
1943 if (row->previous() && row->previous()->par() == row->par()) {
1944 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1945 if (z >= row->pos()) {
1946 // set the dimensions of the row above
1947 y -= row->previous()->height();
1949 refresh_row = row->previous();
1950 status(bview, LyXText::NEED_MORE_REFRESH);
1952 breakAgain(bview, row->previous());
1954 // set the cursor again. Otherwise
1955 // dangling pointers are possible
1956 setCursor(bview, cursor.par(), cursor.pos(),
1957 false, cursor.boundary());
1958 selection.cursor = cursor;
1963 int const tmpheight = row->height();
1964 Paragraph::size_type const tmplast = rowLast(row);
1968 breakAgain(bview, row);
1969 if (row->height() == tmpheight && rowLast(row) == tmplast)
1970 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1972 status(bview, LyXText::NEED_MORE_REFRESH);
1974 // check the special right address boxes
1975 if (textclasslist.Style(bview->buffer()->params.textclass,
1976 par->getLayout()).margintype
1977 == MARGIN_RIGHT_ADDRESS_BOX) {
1984 redoDrawingOfParagraph(bview, tmpcursor);
1987 // set the cursor again. Otherwise dangling pointers are possible
1988 // also set the selection
1990 if (selection.set()) {
1992 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1993 false, selection.cursor.boundary());
1994 selection.cursor = cursor;
1995 setCursorIntern(bview, selection.start.par(),
1996 selection.start.pos(),
1997 false, selection.start.boundary());
1998 selection.start = cursor;
1999 setCursorIntern(bview, selection.end.par(),
2000 selection.end.pos(),
2001 false, selection.end.boundary());
2002 selection.end = cursor;
2003 setCursorIntern(bview, last_sel_cursor.par(),
2004 last_sel_cursor.pos(),
2005 false, last_sel_cursor.boundary());
2006 last_sel_cursor = cursor;
2009 setCursorIntern(bview, cursor.par(), cursor.pos(),
2010 false, cursor.boundary());
2014 // returns false if inset wasn't found
2015 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2017 // first check the current paragraph
2018 int pos = cursor.par()->getPositionOfInset(inset);
2020 checkParagraph(bview, cursor.par(), pos);
2024 // check every paragraph
2026 Paragraph * par = firstParagraph();
2028 pos = par->getPositionOfInset(inset);
2030 checkParagraph(bview, par, pos);
2040 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2041 Paragraph::size_type pos,
2042 bool setfont, bool boundary) const
2044 LyXCursor old_cursor = cursor;
2045 setCursorIntern(bview, par, pos, setfont, boundary);
2046 deleteEmptyParagraphMechanism(bview, old_cursor);
2050 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2051 Paragraph::size_type pos, bool boundary) const
2055 cur.boundary(boundary);
2057 // get the cursor y position in text
2059 Row * row = getRow(par, pos, y);
2060 // y is now the beginning of the cursor row
2061 y += row->baseline();
2062 // y is now the cursor baseline
2065 // now get the cursors x position
2067 float fill_separator;
2069 float fill_label_hfill;
2070 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2072 Paragraph::size_type cursor_vpos = 0;
2073 Paragraph::size_type last = rowLastPrintable(row);
2075 if (pos > last + 1) // This shouldn't happen.
2077 else if (pos < row->pos())
2080 if (last < row->pos())
2081 cursor_vpos = row->pos();
2082 else if (pos > last && !boundary)
2083 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2084 ? row->pos() : last + 1;
2085 else if (pos > row->pos() &&
2086 (pos > last || boundary))
2087 /// Place cursor after char at (logical) position pos - 1
2088 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2089 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2091 /// Place cursor before char at (logical) position pos
2092 cursor_vpos = (bidi_level(pos) % 2 == 0)
2093 ? log2vis(pos) : log2vis(pos) + 1;
2095 Paragraph::size_type main_body =
2096 beginningOfMainBody(bview->buffer(), row->par());
2097 if ((main_body > 0) &&
2098 ((main_body-1 > last) ||
2099 !row->par()->isLineSeparator(main_body-1)))
2102 for (Paragraph::size_type vpos = row->pos();
2103 vpos < cursor_vpos; ++vpos) {
2104 pos = vis2log(vpos);
2105 if (main_body > 0 && pos == main_body - 1) {
2106 x += fill_label_hfill +
2107 lyxfont::width(textclasslist.Style(
2108 bview->buffer()->params.textclass,
2109 row->par()->getLayout())
2111 getLabelFont(bview->buffer(), row->par()));
2112 if (row->par()->isLineSeparator(main_body-1))
2113 x -= singleWidth(bview, row->par(),main_body-1);
2115 if (hfillExpansion(bview->buffer(), row, pos)) {
2116 x += singleWidth(bview, row->par(), pos);
2117 if (pos >= main_body)
2120 x += fill_label_hfill;
2121 } else if (row->par()->isSeparator(pos)) {
2122 x += singleWidth(bview, row->par(), pos);
2123 if (pos >= main_body)
2124 x += fill_separator;
2126 x += singleWidth(bview, row->par(), pos);
2135 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2136 Paragraph::size_type pos,
2137 bool setfont, bool boundary) const
2139 InsetText * it = static_cast<InsetText *>(par->inInset());
2141 lyxerr << "InsetText is " << it << endl;
2142 lyxerr << "inset_owner is " << inset_owner << endl;
2143 if (it != inset_owner) {
2144 #warning I belive this code is wrong. (Lgb)
2145 #warning Jürgen, have a look at this. (Lgb)
2146 // Jürgen, would you like to have a look?
2147 // I guess we need to move the outer cursor
2148 // and open and lock the inset (bla bla bla)
2149 // stuff I don't know... so can you have a look?
2151 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2157 setCursor(bview, cursor, par, pos, boundary);
2159 setCurrentFont(bview);
2163 void LyXText::setCurrentFont(BufferView * bview) const
2165 Paragraph::size_type pos = cursor.pos();
2166 if (cursor.boundary() && pos > 0)
2170 if (pos == cursor.par()->size())
2172 else // potentional bug... BUG (Lgb)
2173 if (cursor.par()->isSeparator(pos)) {
2174 if (pos > cursor.row()->pos() &&
2175 bidi_level(pos) % 2 ==
2176 bidi_level(pos - 1) % 2)
2178 else if (pos + 1 < cursor.par()->size())
2184 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2185 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2187 if (cursor.pos() == cursor.par()->size() &&
2188 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2189 !cursor.boundary()) {
2190 Language const * lang =
2191 cursor.par()->getParLanguage(bview->buffer()->params);
2192 current_font.setLanguage(lang);
2193 current_font.setNumber(LyXFont::OFF);
2194 real_current_font.setLanguage(lang);
2195 real_current_font.setNumber(LyXFont::OFF);
2200 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2202 LyXCursor old_cursor = cursor;
2204 setCursorFromCoordinates(bview, cursor, x, y);
2205 setCurrentFont(bview);
2206 deleteEmptyParagraphMechanism(bview, old_cursor);
2210 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2213 // Get the row first.
2215 Row * row = getRowNearY(y);
2217 int const column = getColumnNearX(bview, row, x, bound);
2219 cur.par(row->par());
2220 cur.pos(row->pos() + column);
2222 cur.y(y + row->baseline());
2224 cur.boundary(bound);
2228 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2230 if (cursor.pos() > 0) {
2231 bool boundary = cursor.boundary();
2232 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2233 if (!internal && !boundary &&
2234 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2235 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2236 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2237 Paragraph * par = cursor.par()->previous();
2238 setCursor(bview, par, par->size());
2243 void LyXText::cursorRight(BufferView * bview, bool internal) const
2245 if (!internal && cursor.boundary() &&
2246 !cursor.par()->isNewline(cursor.pos()))
2247 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2248 else if (cursor.pos() < cursor.par()->size()) {
2249 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2251 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2252 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2253 } else if (cursor.par()->next())
2254 setCursor(bview, cursor.par()->next(), 0);
2258 void LyXText::cursorUp(BufferView * bview) const
2260 setCursorFromCoordinates(bview, cursor.x_fix(),
2261 cursor.y() - cursor.row()->baseline() - 1);
2265 void LyXText::cursorDown(BufferView * bview) const
2267 setCursorFromCoordinates(bview, cursor.x_fix(),
2268 cursor.y() - cursor.row()->baseline()
2269 + cursor.row()->height() + 1);
2273 void LyXText::cursorUpParagraph(BufferView * bview) const
2275 if (cursor.pos() > 0) {
2276 setCursor(bview, cursor.par(), 0);
2278 else if (cursor.par()->previous()) {
2279 setCursor(bview, cursor.par()->previous(), 0);
2284 void LyXText::cursorDownParagraph(BufferView * bview) const
2286 if (cursor.par()->next()) {
2287 setCursor(bview, cursor.par()->next(), 0);
2289 setCursor(bview, cursor.par(), cursor.par()->size());
2294 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2295 LyXCursor const & old_cursor) const
2297 // Would be wrong to delete anything if we have a selection.
2298 if (selection.set()) return;
2300 // We allow all kinds of "mumbo-jumbo" when freespacing.
2301 if (textclasslist.Style(bview->buffer()->params.textclass,
2302 old_cursor.par()->getLayout()).free_spacing)
2305 bool deleted = false;
2307 /* Ok I'll put some comments here about what is missing.
2308 I have fixed BackSpace (and thus Delete) to not delete
2309 double-spaces automagically. I have also changed Cut,
2310 Copy and Paste to hopefully do some sensible things.
2311 There are still some small problems that can lead to
2312 double spaces stored in the document file or space at
2313 the beginning of paragraphs. This happens if you have
2314 the cursor betwenn to spaces and then save. Or if you
2315 cut and paste and the selection have a space at the
2316 beginning and then save right after the paste. I am
2317 sure none of these are very hard to fix, but I will
2318 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2319 that I can get some feedback. (Lgb)
2322 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2323 // delete the LineSeparator.
2326 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2327 // delete the LineSeparator.
2330 // If the pos around the old_cursor were spaces, delete one of them.
2331 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2332 // Only if the cursor has really moved
2334 if (old_cursor.pos() > 0
2335 && old_cursor.pos() < old_cursor.par()->size()
2336 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2337 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2338 old_cursor.par()->erase(old_cursor.pos() - 1);
2339 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2341 if (old_cursor.par() == cursor.par() &&
2342 cursor.pos() > old_cursor.pos()) {
2343 setCursorIntern(bview, cursor.par(),
2346 setCursorIntern(bview, cursor.par(),
2352 // Do not delete empty paragraphs with keepempty set.
2353 if ((textclasslist.Style(bview->buffer()->params.textclass,
2354 old_cursor.par()->getLayout())).keepempty)
2357 LyXCursor tmpcursor;
2359 if (old_cursor.par() != cursor.par()) {
2360 if ((old_cursor.par()->size() == 0
2361 || (old_cursor.par()->size() == 1
2362 && old_cursor.par()->isLineSeparator(0)))) {
2363 // ok, we will delete anything
2365 // make sure that you do not delete any environments
2366 status(bview, LyXText::NEED_MORE_REFRESH);
2369 if (old_cursor.row()->previous()) {
2370 refresh_row = old_cursor.row()->previous();
2371 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2373 cursor = old_cursor; // that undo can restore the right cursor position
2374 Paragraph * endpar = old_cursor.par()->next();
2375 if (endpar && endpar->getDepth()) {
2376 while (endpar && endpar->getDepth()) {
2377 endpar = endpar->next();
2380 setUndo(bview, Undo::DELETE,
2386 removeRow(old_cursor.row());
2387 if (ownerParagraph() == old_cursor.par()) {
2388 ownerParagraph(ownerParagraph()->next());
2391 delete old_cursor.par();
2393 /* Breakagain the next par. Needed
2394 * because of the parindent that
2395 * can occur or dissappear. The
2396 * next row can change its height,
2397 * if there is another layout before */
2398 if (refresh_row->next()) {
2399 breakAgain(bview, refresh_row->next());
2400 updateCounters(bview, refresh_row);
2402 setHeightOfRow(bview, refresh_row);
2404 refresh_row = old_cursor.row()->next();
2405 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2408 cursor = old_cursor; // that undo can restore the right cursor position
2409 Paragraph * endpar = old_cursor.par()->next();
2410 if (endpar && endpar->getDepth()) {
2411 while (endpar && endpar->getDepth()) {
2412 endpar = endpar->next();
2415 setUndo(bview, Undo::DELETE,
2421 removeRow(old_cursor.row());
2423 if (ownerParagraph() == old_cursor.par()) {
2424 ownerParagraph(ownerParagraph()->next());
2427 delete old_cursor.par();
2429 /* Breakagain the next par. Needed
2430 because of the parindent that can
2431 occur or dissappear.
2432 The next row can change its height,
2433 if there is another layout before
2436 breakAgain(bview, refresh_row);
2437 updateCounters(bview, refresh_row->previous());
2443 setCursorIntern(bview, cursor.par(), cursor.pos());
2445 if (selection.cursor.par() == old_cursor.par()
2446 && selection.cursor.pos() == selection.cursor.pos()) {
2447 // correct selection
2448 selection.cursor = cursor;
2452 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2453 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2455 setCursorIntern(bview, cursor.par(), cursor.pos());
2456 selection.cursor = cursor;
2463 void LyXText::toggleAppendix(BufferView * bview)
2465 Paragraph * par = cursor.par();
2466 bool start = !par->params().startOfAppendix();
2468 // ensure that we have only one start_of_appendix in this document
2469 Paragraph * tmp = firstParagraph();
2470 for (; tmp; tmp = tmp->next()) {
2471 tmp->params().startOfAppendix(false);
2474 par->params().startOfAppendix(start);
2476 // we can set the refreshing parameters now
2477 status(bview, LyXText::NEED_MORE_REFRESH);
2479 refresh_row = 0; // not needed for full update
2480 updateCounters(bview, 0);
2481 setCursor(bview, cursor.par(), cursor.pos());
2485 Paragraph * LyXText::ownerParagraph() const
2488 return inset_owner->paragraph();
2490 return bv_owner->buffer()->paragraph;
2494 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2497 inset_owner->paragraph(p);
2499 bv_owner->buffer()->paragraph = p;
2504 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2506 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2507 if (op && op->inInset()) {
2508 static_cast<InsetText *>(op->inInset())->paragraph(p);
2511 inset_owner->paragraph(p);
2513 bv_owner->buffer()->paragraph = p;
2520 LyXText::text_status LyXText::status() const
2526 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2529 if ((status_ != NEED_MORE_REFRESH)
2530 || (status_ == NEED_MORE_REFRESH)
2531 && (st != NEED_VERY_LITTLE_REFRESH)) {
2533 if (inset_owner && st != UNCHANGED) {
2534 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2538 #warning Please tell what the intention is here. (Lgb)
2539 // The above does not make any sense, I changed it to what is here,
2540 // but it still does not make much sense. (Lgb)
2541 #warning Sure have a look now! (Jug)
2542 // well as much as I know && binds more then || so the above and the
2543 // below are identical (this for your known use of parentesis!)
2544 // Now some explanation:
2545 // We should only go up with refreshing code so this means that if
2546 // we have a MORE refresh we should never set it to LITTLE if we still
2547 // didn't handle it (and then it will be UNCHANGED. Now as long as
2548 // we stay inside one LyXText this may work but we need to tell the
2549 // outermost LyXText that it should REALLY draw us if there is some
2550 // change in a Inset::LyXText. So you see that when we are inside a
2551 // inset's LyXText we give the LITTLE to the outermost LyXText to
2552 // tell'em that it should redraw the actual row (where the inset
2553 // resides! Capito?!
2555 if ((status_ != NEED_MORE_REFRESH)
2556 || (status_ == NEED_MORE_REFRESH
2557 && st != NEED_VERY_LITTLE_REFRESH))
2560 if (inset_owner && st != UNCHANGED) {
2561 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2562 if (!bview->text->refresh_row) {
2563 bview->text->refresh_row = bview->text->cursor.row();
2564 bview->text->refresh_y = bview->text->cursor.y() -
2565 bview->text->cursor.row()->baseline();