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 "frontends/LyXView.h"
21 #include "undo_funcs.h"
23 #include "bufferparams.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
32 #include "FloatList.h"
34 #include "ParagraphParameters.h"
37 #include "insets/inseterror.h"
38 #include "insets/insetbib.h"
39 #include "insets/insetspecialchar.h"
40 #include "insets/insettext.h"
41 #include "insets/insetfloat.h"
43 #include "support/LAssert.h"
44 #include "support/textutils.h"
45 #include "support/lstrings.h"
55 LyXText::LyXText(BufferView * bv)
56 : number_of_rows(0), height(0), width(0), first_y(0),
57 bv_owner(bv), inset_owner(0), the_locking_inset(0),
58 need_break_row(0), refresh_y(0), refresh_row(0),
59 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
63 LyXText::LyXText(InsetText * inset)
64 : number_of_rows(0), height(0), width(0), first_y(0),
65 bv_owner(0), inset_owner(inset), the_locking_inset(0),
66 need_break_row(0), refresh_y(0), refresh_row(0),
67 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
71 void LyXText::init(BufferView * bview, bool reinit)
74 // Delete all rows, this does not touch the paragraphs!
75 Row * tmprow = firstrow;
77 tmprow = firstrow->next();
86 copylayouttype.erase();
87 number_of_rows = first_y = refresh_y = 0;
88 status_ = LyXText::UNCHANGED;
92 Paragraph * par = ownerParagraph();
93 current_font = getFont(bview->buffer(), par, 0);
96 insertParagraph(bview, par, lastrow);
99 setCursorIntern(bview, firstrow->par(), 0);
100 selection.cursor = cursor;
106 // Delete all rows, this does not touch the paragraphs!
107 Row * tmprow = firstrow;
109 tmprow = firstrow->next();
118 LyXFont const realizeFont(LyXFont const & font,
122 LyXTextClass const & tclass = buf->params.getLyXTextClass();
123 LyXFont tmpfont(font);
124 Paragraph::depth_type par_depth = par->getDepth();
126 // Resolve against environment font information
127 while (par && par_depth && !tmpfont.resolved()) {
128 par = par->outerHook();
130 #ifndef INHERIT_LANGUAGE
131 tmpfont.realize(par->layout()->font);
133 tmpfont.realize(tclass[par->layout()]->font,
134 buf->params.language);
136 par_depth = par->getDepth();
140 #ifndef INHERIT_LANGUAGE
141 tmpfont.realize(tclass.defaultfont());
143 tmpfont.realize(tclass.defaultfont(), buf->params.language);
152 // Gets the fully instantiated font at a given position in a paragraph
153 // Basically the same routine as Paragraph::getFont() in paragraph.C.
154 // The difference is that this one is used for displaying, and thus we
155 // are allowed to make cosmetic improvements. For instance make footnotes
157 // If position is -1, we get the layout font of the paragraph.
158 // If position is -2, we get the font of the manual label of the paragraph.
159 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
162 lyx::Assert(pos >= 0);
164 LyXLayout_ptr const & layout = par->layout();
166 // We specialize the 95% common case:
167 if (!par->getDepth()) {
168 if (layout->labeltype == LABEL_MANUAL
169 && pos < beginningOfMainBody(buf, par)) {
171 LyXFont f = par->getFontSettings(buf->params, pos);
173 par->inInset()->getDrawFont(f);
174 #ifndef INHERIT_LANGUAGE
175 return f.realize(layout->reslabelfont);
177 return f.realize(layout.reslabelfont, buf->params.language);
180 LyXFont f = par->getFontSettings(buf->params, pos);
182 par->inInset()->getDrawFont(f);
183 #ifndef INHERIT_LANGUAGE
184 return f.realize(layout->resfont);
186 return f.realize(layout.resfont, buf->params.language);
191 // The uncommon case need not be optimized as much
195 if (pos < beginningOfMainBody(buf, par)) {
197 layoutfont = layout->labelfont;
200 layoutfont = layout->font;
203 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
204 #ifndef INHERIT_LANGUAGE
205 tmpfont.realize(layoutfont);
207 tmpfont.realize(layoutfont, buf->params.language);
210 par->inInset()->getDrawFont(tmpfont);
212 return realizeFont(tmpfont, buf, par);
216 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
218 LyXLayout_ptr const & layout = par->layout();
220 if (!par->getDepth()) {
221 return layout->resfont;
224 return realizeFont(layout->font, buf, par);
228 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
230 LyXLayout_ptr const & layout = par->layout();
232 if (!par->getDepth()) {
233 return layout->reslabelfont;
236 return realizeFont(layout->labelfont, buf, par);
240 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
241 pos_type pos, LyXFont const & fnt,
244 Buffer const * buf = bv->buffer();
245 LyXFont font = getFont(buf, par, pos);
246 font.update(fnt, buf->params.language, toggleall);
247 // Let the insets convert their font
248 if (par->isInset(pos)) {
249 Inset * inset = par->getInset(pos);
250 if (isEditableInset(inset)) {
251 UpdatableInset * uinset =
252 static_cast<UpdatableInset *>(inset);
253 uinset->setFont(bv, fnt, toggleall, true);
257 // Plug thru to version below:
258 setCharFont(buf, par, pos, font);
262 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
263 pos_type pos, LyXFont const & fnt)
267 LyXTextClass const & tclass = buf->params.getLyXTextClass();
268 LyXLayout_ptr const & layout = par->layout();
270 // Get concrete layout font to reduce against
273 if (pos < beginningOfMainBody(buf, par))
274 layoutfont = layout->labelfont;
276 layoutfont = layout->font;
278 // Realize against environment font information
279 if (par->getDepth()) {
280 Paragraph * tp = par;
281 while (!layoutfont.resolved() && tp && tp->getDepth()) {
282 tp = tp->outerHook();
284 #ifndef INHERIT_LANGUAGE
285 layoutfont.realize(tp->layout()->font);
287 layoutfont.realize(tclass[tp->layout()].font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(tclass.defaultfont());
296 layoutfont.realize(tclass.defaultfont(), buf->params.language);
299 // Now, reduce font against full layout font
300 font.reduce(layoutfont);
302 par->setFont(pos, font);
306 // inserts a new row behind the specified row, increments
307 // the touched counters
308 void LyXText::insertRow(Row * row, Paragraph * par,
311 Row * tmprow = new Row;
314 tmprow->next(firstrow);
317 tmprow->previous(row);
318 tmprow->next(row->next());
323 tmprow->next()->previous(tmprow);
325 if (tmprow->previous())
326 tmprow->previous()->next(tmprow);
338 // removes the row and reset the touched counters
339 void LyXText::removeRow(Row * row) const
341 Row * row_prev = row->previous();
343 row->next()->previous(row_prev);
345 firstrow = row->next();
346 // lyx::Assert(firstrow);
348 row_prev->next(row->next());
350 if (row == lastrow) {
351 lyx::Assert(!row->next());
354 if (refresh_row == row) {
355 refresh_row = row_prev ? row_prev : row->next();
356 // what about refresh_y, refresh_height
359 height -= row->height(); // the text becomes smaller
362 --number_of_rows; // one row less
366 // remove all following rows of the paragraph of the specified row.
367 void LyXText::removeParagraph(Row * row) const
369 Paragraph * tmppar = row->par();
373 while (row && row->par() == tmppar) {
374 tmprow = row->next();
381 // insert the specified paragraph behind the specified row
382 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
385 insertRow(row, par, 0); /* insert a new row, starting
388 setCounter(bview->buffer(), par); // set the counters
390 // and now append the whole paragraph behind the new row
393 appendParagraph(bview, firstrow);
395 row->next()->height(0);
396 appendParagraph(bview, row->next());
401 Inset * LyXText::getInset() const
404 if (cursor.pos() == 0 && cursor.par()->bibkey) {
405 inset = cursor.par()->bibkey;
406 } else if (cursor.pos() < cursor.par()->size()
407 && cursor.par()->isInset(cursor.pos())) {
408 inset = cursor.par()->getInset(cursor.pos());
414 void LyXText::toggleInset(BufferView * bview)
416 Inset * inset = getInset();
417 // is there an editable inset at cursor position?
418 if (!isEditableInset(inset)) {
419 // No, try to see if we are inside a collapsable inset
420 if (inset_owner && inset_owner->owner()
421 && inset_owner->owner()->isOpen()) {
422 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
423 inset_owner->owner()->close(bview);
424 bview->getLyXText()->cursorRight(bview);
428 //bview->owner()->message(inset->editMessage());
430 // do we want to keep this?? (JMarc)
431 if (!isHighlyEditableInset(inset))
432 setCursorParUndo(bview);
434 if (inset->isOpen()) {
440 inset->open(bview, !inset->isOpen());
445 /* used in setlayout */
446 // Asger is not sure we want to do this...
447 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
450 LyXLayout_ptr const & layout = par->layout();
453 for (pos_type pos = 0; pos < par->size(); ++pos) {
454 if (pos < beginningOfMainBody(buf, par))
455 layoutfont = layout->labelfont;
457 layoutfont = layout->font;
459 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
460 tmpfont.reduce(layoutfont);
461 par->setFont(pos, tmpfont);
466 Paragraph * LyXText::setLayout(BufferView * bview,
467 LyXCursor & cur, LyXCursor & sstart_cur,
468 LyXCursor & send_cur,
469 string const & layout)
471 Paragraph * endpar = send_cur.par()->next();
472 Paragraph * undoendpar = endpar;
474 if (endpar && endpar->getDepth()) {
475 while (endpar && endpar->getDepth()) {
476 endpar = endpar->next();
480 endpar = endpar->next(); // because of parindents etc.
483 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
485 // ok we have a selection. This is always between sstart_cur
486 // and sel_end cursor
488 Paragraph * par = sstart_cur.par();
489 Paragraph * epar = send_cur.par()->next();
491 LyXLayout_ptr const & lyxlayout =
492 bview->buffer()->params.getLyXTextClass()[layout];
495 par->applyLayout(lyxlayout);
496 makeFontEntriesLayoutSpecific(bview->buffer(), par);
497 Paragraph * fppar = par;
498 fppar->params().spaceTop(lyxlayout->fill_top ?
499 VSpace(VSpace::VFILL)
500 : VSpace(VSpace::NONE));
501 fppar->params().spaceBottom(lyxlayout->fill_bottom ?
502 VSpace(VSpace::VFILL)
503 : VSpace(VSpace::NONE));
504 if (lyxlayout->margintype == MARGIN_MANUAL)
505 par->setLabelWidthString(lyxlayout->labelstring());
506 if (lyxlayout->labeltype != LABEL_BIBLIO
508 delete fppar->bibkey;
513 } while (par != epar);
519 // set layout over selection and make a total rebreak of those paragraphs
520 void LyXText::setLayout(BufferView * bview, string const & layout)
522 LyXCursor tmpcursor = cursor; /* store the current cursor */
524 // if there is no selection just set the layout
525 // of the current paragraph */
526 if (!selection.set()) {
527 selection.start = cursor; // dummy selection
528 selection.end = cursor;
530 Paragraph * endpar = setLayout(bview, cursor, selection.start,
531 selection.end, layout);
532 redoParagraphs(bview, selection.start, endpar);
534 // we have to reset the selection, because the
535 // geometry could have changed
536 setCursor(bview, selection.start.par(),
537 selection.start.pos(), false);
538 selection.cursor = cursor;
539 setCursor(bview, selection.end.par(), selection.end.pos(), false);
540 updateCounters(bview);
543 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
547 // increment depth over selection and
548 // make a total rebreak of those paragraphs
549 void LyXText::incDepth(BufferView * bview)
551 // If there is no selection, just use the current paragraph
552 if (!selection.set()) {
553 selection.start = cursor; // dummy selection
554 selection.end = cursor;
557 // We end at the next paragraph with depth 0
558 Paragraph * endpar = selection.end.par()->next();
560 Paragraph * undoendpar = endpar;
562 if (endpar && endpar->getDepth()) {
563 while (endpar && endpar->getDepth()) {
564 endpar = endpar->next();
568 endpar = endpar->next(); // because of parindents etc.
571 setUndo(bview, Undo::EDIT,
572 selection.start.par(), undoendpar);
574 LyXCursor tmpcursor = cursor; // store the current cursor
576 // ok we have a selection. This is always between sel_start_cursor
577 // and sel_end cursor
578 cursor = selection.start;
580 bool anything_changed = false;
583 // NOTE: you can't change the depth of a bibliography entry
584 if (cursor.par()->layout()->labeltype != LABEL_BIBLIO) {
585 Paragraph * prev = cursor.par()->previous();
588 if (cursor.par()->getDepth()
589 < prev->getMaxDepthAfter()) {
590 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
591 anything_changed = true;
595 if (cursor.par() == selection.end.par())
597 cursor.par(cursor.par()->next());
600 // if nothing changed set all depth to 0
601 if (!anything_changed) {
602 cursor = selection.start;
603 while (cursor.par() != selection.end.par()) {
604 cursor.par()->params().depth(0);
605 cursor.par(cursor.par()->next());
607 cursor.par()->params().depth(0);
610 redoParagraphs(bview, selection.start, endpar);
612 // we have to reset the selection, because the
613 // geometry could have changed
614 setCursor(bview, selection.start.par(), selection.start.pos());
615 selection.cursor = cursor;
616 setCursor(bview, selection.end.par(), selection.end.pos());
617 updateCounters(bview);
620 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
624 // decrement depth over selection and
625 // make a total rebreak of those paragraphs
626 void LyXText::decDepth(BufferView * bview)
628 // if there is no selection just set the layout
629 // of the current paragraph
630 if (!selection.set()) {
631 selection.start = cursor; // dummy selection
632 selection.end = cursor;
634 Paragraph * endpar = selection.end.par()->next();
635 Paragraph * undoendpar = endpar;
637 if (endpar && endpar->getDepth()) {
638 while (endpar && endpar->getDepth()) {
639 endpar = endpar->next();
643 endpar = endpar->next(); // because of parindents etc.
646 setUndo(bview, Undo::EDIT,
647 selection.start.par(), undoendpar);
649 LyXCursor tmpcursor = cursor; // store the current cursor
651 // ok we have a selection. This is always between sel_start_cursor
652 // and sel_end cursor
653 cursor = selection.start;
656 if (cursor.par()->params().depth()) {
657 cursor.par()->params()
658 .depth(cursor.par()->params().depth() - 1);
660 if (cursor.par() == selection.end.par()) {
663 cursor.par(cursor.par()->next());
666 redoParagraphs(bview, selection.start, endpar);
668 // we have to reset the selection, because the
669 // geometry could have changed
670 setCursor(bview, selection.start.par(),
671 selection.start.pos());
672 selection.cursor = cursor;
673 setCursor(bview, selection.end.par(), selection.end.pos());
674 updateCounters(bview);
677 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
681 // set font over selection and make a total rebreak of those paragraphs
682 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
684 // if there is no selection just set the current_font
685 if (!selection.set()) {
686 // Determine basis font
688 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
690 layoutfont = getLabelFont(bview->buffer(),
693 layoutfont = getLayoutFont(bview->buffer(),
696 // Update current font
697 real_current_font.update(font,
698 bview->buffer()->params.language,
701 // Reduce to implicit settings
702 current_font = real_current_font;
703 current_font.reduce(layoutfont);
704 // And resolve it completely
705 #ifndef INHERIT_LANGUAGE
706 real_current_font.realize(layoutfont);
708 real_current_font.realize(layoutfont,
709 bview->buffer()->params.language);
714 LyXCursor tmpcursor = cursor; // store the current cursor
716 // ok we have a selection. This is always between sel_start_cursor
717 // and sel_end cursor
719 setUndo(bview, Undo::EDIT,
720 selection.start.par(), selection.end.par()->next());
722 cursor = selection.start;
723 while (cursor.par() != selection.end.par() ||
724 cursor.pos() < selection.end.pos())
726 if (cursor.pos() < cursor.par()->size()) {
727 // an open footnote should behave like a closed one
728 setCharFont(bview, cursor.par(), cursor.pos(),
730 cursor.pos(cursor.pos() + 1);
733 cursor.par(cursor.par()->next());
738 redoParagraphs(bview, selection.start, selection.end.par()->next());
740 // we have to reset the selection, because the
741 // geometry could have changed, but we keep
742 // it for user convenience
743 setCursor(bview, selection.start.par(), selection.start.pos());
744 selection.cursor = cursor;
745 setCursor(bview, selection.end.par(), selection.end.pos());
747 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
748 tmpcursor.boundary());
752 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
754 Row * tmprow = cur.row();
755 int y = cur.y() - tmprow->baseline();
757 setHeightOfRow(bview, tmprow);
759 while (tmprow->previous()
760 && tmprow->previous()->par() == tmprow->par()) {
761 tmprow = tmprow->previous();
762 y -= tmprow->height();
763 setHeightOfRow(bview, tmprow);
766 // we can set the refreshing parameters now
767 status(bview, LyXText::NEED_MORE_REFRESH);
769 refresh_row = tmprow;
770 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
774 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
776 Row * tmprow = cur.row();
778 int y = cur.y() - tmprow->baseline();
779 setHeightOfRow(bview, tmprow);
781 while (tmprow->previous()
782 && tmprow->previous()->par() == tmprow->par()) {
783 tmprow = tmprow->previous();
784 y -= tmprow->height();
787 // we can set the refreshing parameters now
788 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
790 refresh_row = tmprow;
792 status(bview, LyXText::NEED_MORE_REFRESH);
793 setCursor(bview, cur.par(), cur.pos());
797 // deletes and inserts again all paragaphs between the cursor
798 // and the specified par
799 // This function is needed after SetLayout and SetFont etc.
800 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
801 Paragraph const * endpar) const
804 Paragraph * tmppar = 0;
805 Paragraph * first_phys_par = 0;
807 Row * tmprow = cur.row();
809 int y = cur.y() - tmprow->baseline();
811 if (!tmprow->previous()) {
812 // a trick/hack for UNDO
813 // This is needed because in an UNDO/REDO we could have changed
814 // the ownerParagrah() so the paragraph inside the row is NOT
815 // my really first par anymore. Got it Lars ;) (Jug 20011206)
816 first_phys_par = ownerParagraph();
818 first_phys_par = tmprow->par();
819 while (tmprow->previous()
820 && tmprow->previous()->par() == first_phys_par)
822 tmprow = tmprow->previous();
823 y -= tmprow->height();
827 // we can set the refreshing parameters now
828 status(bview, LyXText::NEED_MORE_REFRESH);
830 refresh_row = tmprow->previous(); /* the real refresh row will
831 be deleted, so I store
835 tmppar = tmprow->next()->par();
838 while (tmprow->next() && tmppar != endpar) {
839 removeRow(tmprow->next());
840 if (tmprow->next()) {
841 tmppar = tmprow->next()->par();
847 // remove the first one
848 tmprow2 = tmprow; /* this is because tmprow->previous()
850 tmprow = tmprow->previous();
853 tmppar = first_phys_par;
857 insertParagraph(bview, tmppar, tmprow);
861 while (tmprow->next()
862 && tmprow->next()->par() == tmppar) {
863 tmprow = tmprow->next();
865 tmppar = tmppar->next();
867 } while (tmppar && tmppar != endpar);
869 // this is because of layout changes
871 refresh_y -= refresh_row->height();
872 setHeightOfRow(bview, refresh_row);
874 refresh_row = firstrow;
876 setHeightOfRow(bview, refresh_row);
879 if (tmprow && tmprow->next())
880 setHeightOfRow(bview, tmprow->next());
881 updateCounters(bview);
885 void LyXText::fullRebreak(BufferView * bview)
891 if (need_break_row) {
892 breakAgain(bview, need_break_row);
899 // important for the screen
902 // the cursor set functions have a special mechanism. When they
903 // realize, that you left an empty paragraph, they will delete it.
904 // They also delete the corresponding row
906 // need the selection cursor:
907 void LyXText::setSelection(BufferView * bview)
909 bool const lsel = selection.set();
911 if (!selection.set()) {
912 last_sel_cursor = selection.cursor;
913 selection.start = selection.cursor;
914 selection.end = selection.cursor;
919 // first the toggling area
920 if (cursor.y() < last_sel_cursor.y()
921 || (cursor.y() == last_sel_cursor.y()
922 && cursor.x() < last_sel_cursor.x())) {
923 toggle_end_cursor = last_sel_cursor;
924 toggle_cursor = cursor;
926 toggle_end_cursor = cursor;
927 toggle_cursor = last_sel_cursor;
930 last_sel_cursor = cursor;
932 // and now the whole selection
934 if (selection.cursor.par() == cursor.par())
935 if (selection.cursor.pos() < cursor.pos()) {
936 selection.end = cursor;
937 selection.start = selection.cursor;
939 selection.end = selection.cursor;
940 selection.start = cursor;
942 else if (selection.cursor.y() < cursor.y() ||
943 (selection.cursor.y() == cursor.y()
944 && selection.cursor.x() < cursor.x())) {
945 selection.end = cursor;
946 selection.start = selection.cursor;
949 selection.end = selection.cursor;
950 selection.start = cursor;
953 // a selection with no contents is not a selection
954 if (selection.start.par() == selection.end.par() &&
955 selection.start.pos() == selection.end.pos())
956 selection.set(false);
958 if (inset_owner && (selection.set() || lsel))
959 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
963 string const LyXText::selectionAsString(Buffer const * buffer,
966 if (!selection.set()) return string();
968 // should be const ...
969 Paragraph * startpar(selection.start.par());
970 Paragraph * endpar(selection.end.par());
971 pos_type const startpos(selection.start.pos());
972 pos_type const endpos(selection.end.pos());
974 if (startpar == endpar) {
975 return startpar->asString(buffer, startpos, endpos, label);
980 // First paragraph in selection
981 result += startpar->asString(buffer, startpos, startpar->size(), label) + "\n\n";
983 // The paragraphs in between (if any)
984 LyXCursor tmpcur(selection.start);
985 tmpcur.par(tmpcur.par()->next());
986 while (tmpcur.par() != endpar) {
987 result += tmpcur.par()->asString(buffer, 0,
988 tmpcur.par()->size(),
990 tmpcur.par(tmpcur.par()->next());
993 // Last paragraph in selection
994 result += endpar->asString(buffer, 0, endpos, label);
1000 void LyXText::clearSelection() const
1002 selection.set(false);
1003 selection.mark(false);
1004 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1005 // reset this in the bv_owner!
1006 if (bv_owner && bv_owner->text)
1007 bv_owner->text->xsel_cache.set(false);
1011 void LyXText::cursorHome(BufferView * bview) const
1013 setCursor(bview, cursor.par(), cursor.row()->pos());
1017 void LyXText::cursorEnd(BufferView * bview) const
1019 if (!cursor.row()->next()
1020 || cursor.row()->next()->par() != cursor.row()->par()) {
1021 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1023 if (!cursor.par()->empty() &&
1024 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1025 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1026 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1028 setCursor(bview,cursor.par(),
1029 rowLast(cursor.row()) + 1);
1035 void LyXText::cursorTop(BufferView * bview) const
1037 while (cursor.par()->previous())
1038 cursor.par(cursor.par()->previous());
1039 setCursor(bview, cursor.par(), 0);
1043 void LyXText::cursorBottom(BufferView * bview) const
1045 while (cursor.par()->next())
1046 cursor.par(cursor.par()->next());
1047 setCursor(bview, cursor.par(), cursor.par()->size());
1051 void LyXText::toggleFree(BufferView * bview,
1052 LyXFont const & font, bool toggleall)
1054 // If the mask is completely neutral, tell user
1055 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1056 // Could only happen with user style
1057 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1061 // Try implicit word selection
1062 // If there is a change in the language the implicit word selection
1064 LyXCursor resetCursor = cursor;
1065 bool implicitSelection = (font.language() == ignore_language
1066 && font.number() == LyXFont::IGNORE)
1067 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1070 setFont(bview, font, toggleall);
1072 // Implicit selections are cleared afterwards
1073 //and cursor is set to the original position.
1074 if (implicitSelection) {
1076 cursor = resetCursor;
1077 setCursor(bview, cursor.par(), cursor.pos());
1078 selection.cursor = cursor;
1081 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1085 string LyXText::getStringToIndex(BufferView * bview)
1089 // Try implicit word selection
1090 // If there is a change in the language the implicit word selection
1092 LyXCursor const reset_cursor = cursor;
1093 bool const implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1095 if (!selection.set()) {
1096 bview->owner()->message(_("Nothing to index!"));
1099 if (selection.start.par() != selection.end.par()) {
1100 bview->owner()->message(_("Cannot index more than one paragraph!"));
1104 idxstring = selectionAsString(bview->buffer(), false);
1106 // Implicit selections are cleared afterwards
1107 //and cursor is set to the original position.
1108 if (implicitSelection) {
1110 cursor = reset_cursor;
1111 setCursor(bview, cursor.par(), cursor.pos());
1112 selection.cursor = cursor;
1118 pos_type LyXText::beginningOfMainBody(Buffer const * /*buf*/,
1119 Paragraph const * par) const
1121 if (par->layout()->labeltype != LABEL_MANUAL)
1124 return par->beginningOfMainBody();
1128 // the DTP switches for paragraphs. LyX will store them in the first
1129 // physicla paragraph. When a paragraph is broken, the top settings rest,
1130 // the bottom settings are given to the new one. So I can make shure,
1131 // they do not duplicate themself and you cannnot make dirty things with
1134 void LyXText::setParagraph(BufferView * bview,
1135 bool line_top, bool line_bottom,
1136 bool pagebreak_top, bool pagebreak_bottom,
1137 VSpace const & space_top,
1138 VSpace const & space_bottom,
1139 Spacing const & spacing,
1141 string labelwidthstring,
1144 LyXCursor tmpcursor = cursor;
1145 if (!selection.set()) {
1146 selection.start = cursor;
1147 selection.end = cursor;
1150 // make sure that the depth behind the selection are restored, too
1151 Paragraph * endpar = selection.end.par()->next();
1152 Paragraph * undoendpar = endpar;
1154 if (endpar && endpar->getDepth()) {
1155 while (endpar && endpar->getDepth()) {
1156 endpar = endpar->next();
1157 undoendpar = endpar;
1161 // because of parindents etc.
1162 endpar = endpar->next();
1165 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1168 Paragraph * tmppar = selection.end.par();
1170 while (tmppar != selection.start.par()->previous()) {
1171 setCursor(bview, tmppar, 0);
1172 status(bview, LyXText::NEED_MORE_REFRESH);
1173 refresh_row = cursor.row();
1174 refresh_y = cursor.y() - cursor.row()->baseline();
1175 cursor.par()->params().lineTop(line_top);
1176 cursor.par()->params().lineBottom(line_bottom);
1177 cursor.par()->params().pagebreakTop(pagebreak_top);
1178 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1179 cursor.par()->params().spaceTop(space_top);
1180 cursor.par()->params().spaceBottom(space_bottom);
1181 cursor.par()->params().spacing(spacing);
1182 // does the layout allow the new alignment?
1183 LyXLayout_ptr const & layout = cursor.par()->layout();
1185 if (align == LYX_ALIGN_LAYOUT)
1186 align = layout->align;
1187 if (align & layout->alignpossible) {
1188 if (align == layout->align)
1189 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1191 cursor.par()->params().align(align);
1193 cursor.par()->setLabelWidthString(labelwidthstring);
1194 cursor.par()->params().noindent(noindent);
1195 tmppar = cursor.par()->previous();
1198 redoParagraphs(bview, selection.start, endpar);
1201 setCursor(bview, selection.start.par(), selection.start.pos());
1202 selection.cursor = cursor;
1203 setCursor(bview, selection.end.par(), selection.end.pos());
1204 setSelection(bview);
1205 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1207 bview->updateInset(inset_owner, true);
1211 // set the counter of a paragraph. This includes the labels
1212 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1214 LyXTextClass const & textclass = buf->params.getLyXTextClass();
1215 LyXLayout_ptr const & layout = par->layout();
1217 if (par->previous()) {
1219 par->params().appendix(par->previous()->params().appendix());
1220 if (!par->params().appendix() && par->params().startOfAppendix()) {
1221 par->params().appendix(true);
1222 buf->counters().reset();
1224 par->enumdepth = par->previous()->enumdepth;
1225 par->itemdepth = par->previous()->itemdepth;
1227 par->params().appendix(par->params().startOfAppendix());
1232 /* Maybe we have to increment the enumeration depth.
1233 * BUT, enumeration in a footnote is considered in isolation from its
1234 * surrounding paragraph so don't increment if this is the
1235 * first line of the footnote
1236 * AND, bibliographies can't have their depth changed ie. they
1237 * are always of depth 0
1240 && par->previous()->getDepth() < par->getDepth()
1241 && par->previous()->layout()->labeltype == LABEL_COUNTER_ENUMI
1242 && par->enumdepth < 3
1243 && layout->labeltype != LABEL_BIBLIO) {
1247 // Maybe we have to decrement the enumeration depth, see note above
1249 && par->previous()->getDepth() > par->getDepth()
1250 && layout->labeltype != LABEL_BIBLIO) {
1251 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1254 if (!par->params().labelString().empty()) {
1255 par->params().labelString(string());
1258 if (layout->margintype == MARGIN_MANUAL) {
1259 if (par->params().labelWidthString().empty()) {
1260 par->setLabelWidthString(layout->labelstring());
1263 par->setLabelWidthString(string());
1266 // is it a layout that has an automatic label?
1267 if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1269 int i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1270 string numbertype, langtype;
1273 if (i >= 0 && i<= buf->params.secnumdepth) {
1275 buf->counters().step(buf->counters().sects[i]);
1277 // Is there a label? Useful for Chapter layout
1278 if (!par->params().appendix()) {
1279 if (!layout->labelstring().empty())
1280 par->params().labelString(layout->labelstring());
1282 par->params().labelString(string());
1284 if (!layout->labelstring_appendix().empty())
1285 par->params().labelString(layout->labelstring_appendix());
1287 par->params().labelString(string());
1290 // Use if an integer is here less than elegant. For now.
1291 int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1292 if (!par->params().appendix()) {
1293 numbertype = "sectioning";
1295 numbertype = "appendix";
1296 if (par->isRightToLeftPar(buf->params))
1297 langtype = "hebrew";
1302 s << buf->counters().numberLabel(buf->counters().sects[i],
1303 numbertype, langtype, head);
1305 par->params().labelString(par->params().labelString() + s.str().c_str());
1306 // We really want to remove the c_str as soon as
1309 // reset enum counters
1310 buf->counters().reset("enum");
1311 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1312 buf->counters().reset("enum");
1313 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1314 buf->counters().step(buf->counters().enums[par->enumdepth]);
1316 s << buf->counters().numberLabel(buf->counters().enums[par->enumdepth],
1317 "enumeration", langtype);
1318 par->params().labelString(s.str().c_str());
1321 } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1322 buf->counters().step("bibitem");
1323 int number = buf->counters().value("bibitem");
1325 InsetCommandParams p("bibitem" );
1326 par->bibkey = new InsetBibKey(p);
1328 par->bibkey->setCounter(number);
1329 par->params().labelString(layout->labelstring());
1331 // In biblio should't be following counters but...
1333 string s = layout->labelstring();
1335 // the caption hack:
1336 if (layout->labeltype == LABEL_SENSITIVE) {
1337 Paragraph * tmppar = par;
1340 while (tmppar && tmppar->inInset()
1341 // the single '=' is intended below
1342 && (in = tmppar->inInset()->owner())) {
1343 if (in->lyxCode() == Inset::FLOAT_CODE) {
1347 tmppar = in->parOwner();
1353 = floatList.getType(static_cast<InsetFloat*>(in)->type());
1355 buf->counters().step(fl.name());
1357 // Doesn't work... yet.
1359 //o << fl.name() << " " << buf->counters().value(fl.name()) << ":";
1360 o << fl.name() << " #:";
1363 /* par->SetLayout(0);
1364 s = layout->labelstring; */
1365 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1366 ? " :úåòîùî øñç" : "Senseless: ";
1369 par->params().labelString(s);
1371 /* reset the enumeration counter. They are always resetted
1372 * when there is any other layout between */
1373 for (int i = par->enumdepth + 1; i < 4; i++) {
1374 buf->counters().set(buf->counters().enums[i], 0);
1380 // Updates all counters BEHIND the row. Changed paragraphs
1381 // with a dynamic left margin will be rebroken.
1382 void LyXText::updateCounters(BufferView * bview) const
1386 Row * row = firstrow;
1389 bview->buffer()->counters().reset();
1391 while (row->par() != par)
1394 setCounter(bview->buffer(), par);
1396 // now check for the headline layouts. remember that they
1397 // have a dynamic left margin
1398 LyXLayout_ptr const & layout = par->layout();
1400 if (layout->margintype == MARGIN_DYNAMIC
1401 || layout->labeltype == LABEL_SENSITIVE) {
1402 // Rebreak the paragraph
1403 removeParagraph(row);
1404 appendParagraph(bview, row);
1411 void LyXText::insertInset(BufferView * bview, Inset * inset)
1413 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1415 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1417 cursor.par()->insertInset(cursor.pos(), inset);
1418 // Just to rebreak and refresh correctly.
1419 // The character will not be inserted a second time
1420 insertChar(bview, Paragraph::META_INSET);
1421 // If we enter a highly editable inset the cursor should be to before
1422 // the inset. This couldn't happen before as Undo was not handled inside
1423 // inset now after the Undo LyX tries to call inset->Edit(...) again
1424 // and cannot do this as the cursor is behind the inset and GetInset
1425 // does not return the inset!
1426 if (isHighlyEditableInset(inset)) {
1427 cursorLeft(bview, true);
1433 void LyXText::copyEnvironmentType()
1435 copylayouttype = cursor.par()->layout()->name();
1439 void LyXText::pasteEnvironmentType(BufferView * bview)
1441 setLayout(bview, copylayouttype);
1445 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1447 // Stuff what we got on the clipboard. Even if there is no selection.
1449 // There is a problem with having the stuffing here in that the
1450 // larger the selection the slower LyX will get. This can be
1451 // solved by running the line below only when the selection has
1452 // finished. The solution used currently just works, to make it
1453 // faster we need to be more clever and probably also have more
1454 // calls to stuffClipboard. (Lgb)
1455 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1457 // This doesn't make sense, if there is no selection
1458 if (!selection.set())
1461 // OK, we have a selection. This is always between selection.start
1462 // and selection.end
1464 // make sure that the depth behind the selection are restored, too
1465 Paragraph * endpar = selection.end.par()->next();
1466 Paragraph * undoendpar = endpar;
1468 if (endpar && endpar->getDepth()) {
1469 while (endpar && endpar->getDepth()) {
1470 endpar = endpar->next();
1471 undoendpar = endpar;
1473 } else if (endpar) {
1474 endpar = endpar->next(); // because of parindents etc.
1477 setUndo(bview, Undo::DELETE,
1478 selection.start.par(), undoendpar);
1480 // there are two cases: cut only within one paragraph or
1481 // more than one paragraph
1482 if (selection.start.par() == selection.end.par()) {
1483 // only within one paragraph
1484 endpar = selection.end.par();
1485 int pos = selection.end.pos();
1486 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1487 selection.start.pos(), pos,
1488 bview->buffer()->params.textclass,
1490 selection.end.pos(pos);
1492 endpar = selection.end.par();
1493 int pos = selection.end.pos();
1494 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1495 selection.start.pos(), pos,
1496 bview->buffer()->params.textclass,
1499 selection.end.par(endpar);
1500 selection.end.pos(pos);
1501 cursor.pos(selection.end.pos());
1503 endpar = endpar->next();
1505 // sometimes necessary
1507 selection.start.par()->stripLeadingSpaces();
1509 redoParagraphs(bview, selection.start, endpar);
1511 // cutSelection can invalidate the cursor so we need to set
1513 cursor = selection.start;
1515 // need a valid cursor. (Lgb)
1518 setCursor(bview, cursor.par(), cursor.pos());
1519 selection.cursor = cursor;
1520 updateCounters(bview);
1524 void LyXText::copySelection(BufferView * bview)
1526 // stuff the selection onto the X clipboard, from an explicit copy request
1527 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1529 // this doesnt make sense, if there is no selection
1530 if (!selection.set())
1533 // ok we have a selection. This is always between selection.start
1534 // and sel_end cursor
1536 // copy behind a space if there is one
1537 while (selection.start.par()->size() > selection.start.pos()
1538 && selection.start.par()->isLineSeparator(selection.start.pos())
1539 && (selection.start.par() != selection.end.par()
1540 || selection.start.pos() < selection.end.pos()))
1541 selection.start.pos(selection.start.pos() + 1);
1543 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1544 selection.start.pos(), selection.end.pos(),
1545 bview->buffer()->params.textclass);
1549 void LyXText::pasteSelection(BufferView * bview)
1551 // this does not make sense, if there is nothing to paste
1552 if (!CutAndPaste::checkPastePossible(cursor.par()))
1555 setUndo(bview, Undo::INSERT,
1556 cursor.par(), cursor.par()->next());
1559 Paragraph * actpar = cursor.par();
1560 int pos = cursor.pos();
1562 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1563 bview->buffer()->params.textclass);
1565 redoParagraphs(bview, cursor, endpar);
1567 setCursor(bview, cursor.par(), cursor.pos());
1570 selection.cursor = cursor;
1571 setCursor(bview, actpar, pos);
1572 setSelection(bview);
1573 updateCounters(bview);
1577 // sets the selection over the number of characters of string, no check!!
1578 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1583 selection.cursor = cursor;
1584 for (string::size_type i = 0; i < str.length(); ++i)
1586 setSelection(bview);
1590 // simple replacing. The font of the first selected character is used
1591 void LyXText::replaceSelectionWithString(BufferView * bview,
1594 setCursorParUndo(bview);
1597 if (!selection.set()) { // create a dummy selection
1598 selection.end = cursor;
1599 selection.start = cursor;
1602 // Get font setting before we cut
1603 pos_type pos = selection.end.pos();
1604 LyXFont const font = selection.start.par()
1605 ->getFontSettings(bview->buffer()->params,
1606 selection.start.pos());
1608 // Insert the new string
1609 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1610 selection.end.par()->insertChar(pos, (*cit), font);
1614 // Cut the selection
1615 cutSelection(bview, true, false);
1621 // needed to insert the selection
1622 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1624 Paragraph * par = cursor.par();
1625 pos_type pos = cursor.pos();
1626 Paragraph * endpar = cursor.par()->next();
1628 setCursorParUndo(bview);
1630 // only to be sure, should not be neccessary
1633 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1635 redoParagraphs(bview, cursor, endpar);
1636 setCursor(bview, cursor.par(), cursor.pos());
1637 selection.cursor = cursor;
1638 setCursor(bview, par, pos);
1639 setSelection(bview);
1643 // turns double-CR to single CR, others where converted into one
1644 // blank. Then InsertStringAsLines is called
1645 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1647 string linestr(str);
1648 bool newline_inserted = false;
1649 for (string::size_type i = 0; i < linestr.length(); ++i) {
1650 if (linestr[i] == '\n') {
1651 if (newline_inserted) {
1652 // we know that \r will be ignored by
1653 // InsertStringA. Of course, it is a dirty
1654 // trick, but it works...
1655 linestr[i - 1] = '\r';
1659 newline_inserted = true;
1661 } else if (IsPrintable(linestr[i])) {
1662 newline_inserted = false;
1665 insertStringAsLines(bview, linestr);
1669 bool LyXText::gotoNextInset(BufferView * bview,
1670 vector<Inset::Code> const & codes,
1671 string const & contents) const
1673 LyXCursor res = cursor;
1676 if (res.pos() < res.par()->size() - 1) {
1677 res.pos(res.pos() + 1);
1679 res.par(res.par()->next());
1683 } while (res.par() &&
1684 !(res.par()->isInset(res.pos())
1685 && (inset = res.par()->getInset(res.pos())) != 0
1686 && find(codes.begin(), codes.end(), inset->lyxCode())
1688 && (contents.empty() ||
1689 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1693 setCursor(bview, res.par(), res.pos(), false);
1700 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1703 LyXCursor tmpcursor;
1707 Row * row = getRow(par, pos, y);
1709 // is there a break one row above
1710 if (row->previous() && row->previous()->par() == row->par()) {
1711 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1712 if (z >= row->pos()) {
1713 // set the dimensions of the row above
1714 y -= row->previous()->height();
1716 refresh_row = row->previous();
1717 status(bview, LyXText::NEED_MORE_REFRESH);
1719 breakAgain(bview, row->previous());
1721 // set the cursor again. Otherwise
1722 // dangling pointers are possible
1723 setCursor(bview, cursor.par(), cursor.pos(),
1724 false, cursor.boundary());
1725 selection.cursor = cursor;
1730 int const tmpheight = row->height();
1731 pos_type const tmplast = rowLast(row);
1735 breakAgain(bview, row);
1736 if (row->height() == tmpheight && rowLast(row) == tmplast)
1737 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1739 status(bview, LyXText::NEED_MORE_REFRESH);
1741 // check the special right address boxes
1742 if (par->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1749 redoDrawingOfParagraph(bview, tmpcursor);
1752 // set the cursor again. Otherwise dangling pointers are possible
1753 // also set the selection
1755 if (selection.set()) {
1757 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1758 false, selection.cursor.boundary());
1759 selection.cursor = cursor;
1760 setCursorIntern(bview, selection.start.par(),
1761 selection.start.pos(),
1762 false, selection.start.boundary());
1763 selection.start = cursor;
1764 setCursorIntern(bview, selection.end.par(),
1765 selection.end.pos(),
1766 false, selection.end.boundary());
1767 selection.end = cursor;
1768 setCursorIntern(bview, last_sel_cursor.par(),
1769 last_sel_cursor.pos(),
1770 false, last_sel_cursor.boundary());
1771 last_sel_cursor = cursor;
1774 setCursorIntern(bview, cursor.par(), cursor.pos(),
1775 false, cursor.boundary());
1779 // returns false if inset wasn't found
1780 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1782 // first check the current paragraph
1783 int pos = cursor.par()->getPositionOfInset(inset);
1785 checkParagraph(bview, cursor.par(), pos);
1789 // check every paragraph
1791 Paragraph * par = ownerParagraph();
1793 pos = par->getPositionOfInset(inset);
1795 checkParagraph(bview, par, pos);
1805 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
1807 bool setfont, bool boundary) const
1809 LyXCursor old_cursor = cursor;
1810 setCursorIntern(bview, par, pos, setfont, boundary);
1811 return deleteEmptyParagraphMechanism(bview, old_cursor);
1815 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
1816 pos_type pos, bool boundary) const
1823 cur.boundary(boundary);
1825 // get the cursor y position in text
1827 Row * row = getRow(par, pos, y);
1828 Row * old_row = row;
1830 // if we are before the first char of this row and are still in the
1831 // same paragraph and there is a previous row then put the cursor on
1832 // the end of the previous row
1833 cur.iy(y + row->baseline());
1835 if (row->previous() && pos &&
1836 row->previous()->par() == row->par() &&
1837 par->getChar(pos) == Paragraph::META_INSET &&
1838 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
1840 row = row->previous();
1845 // y is now the beginning of the cursor row
1846 y += row->baseline();
1847 // y is now the cursor baseline
1850 pos_type last = rowLastPrintable(old_row);
1852 if (pos > last + 1) {
1853 // This shouldn't happen.
1856 } else if (pos < row->pos()) {
1861 // now get the cursors x position
1862 float x = getCursorX(bview, row, pos, last, boundary);
1865 if (old_row != row) {
1866 x = getCursorX(bview, old_row, pos, last, boundary);
1873 float LyXText::getCursorX(BufferView * bview, Row * row,
1874 pos_type pos, pos_type last, bool boundary) const
1876 pos_type cursor_vpos = 0;
1878 float fill_separator;
1880 float fill_label_hfill;
1881 // This call HAS to be here because of the BidiTables!!!
1882 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
1885 if (last < row->pos())
1886 cursor_vpos = row->pos();
1887 else if (pos > last && !boundary)
1888 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
1889 ? row->pos() : last + 1;
1890 else if (pos > row->pos() &&
1891 (pos > last || boundary))
1892 /// Place cursor after char at (logical) position pos - 1
1893 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1894 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1896 /// Place cursor before char at (logical) position pos
1897 cursor_vpos = (bidi_level(pos) % 2 == 0)
1898 ? log2vis(pos) : log2vis(pos) + 1;
1900 pos_type main_body =
1901 beginningOfMainBody(bview->buffer(), row->par());
1902 if ((main_body > 0) &&
1903 ((main_body-1 > last) ||
1904 !row->par()->isLineSeparator(main_body-1)))
1907 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
1908 pos_type pos = vis2log(vpos);
1909 if (main_body > 0 && pos == main_body - 1) {
1910 x += fill_label_hfill +
1911 font_metrics::width(
1912 row->par()->layout()->labelsep,
1913 getLabelFont(bview->buffer(),
1915 if (row->par()->isLineSeparator(main_body - 1))
1916 x -= singleWidth(bview,
1917 row->par(), main_body - 1);
1919 if (hfillExpansion(bview->buffer(), row, pos)) {
1920 x += singleWidth(bview, row->par(), pos);
1921 if (pos >= main_body)
1924 x += fill_label_hfill;
1925 } else if (row->par()->isSeparator(pos)) {
1926 x += singleWidth(bview, row->par(), pos);
1927 if (pos >= main_body)
1928 x += fill_separator;
1930 x += singleWidth(bview, row->par(), pos);
1936 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
1937 pos_type pos, bool setfont, bool boundary) const
1939 InsetText * it = static_cast<InsetText *>(par->inInset());
1941 if (it != inset_owner) {
1942 lyxerr[Debug::INSETS] << "InsetText is " << it
1944 << "inset_owner is "
1945 << inset_owner << endl;
1946 #ifdef WITH_WARNINGS
1947 #warning I believe this code is wrong. (Lgb)
1948 #warning Jürgen, have a look at this. (Lgb)
1949 #warning Hmmm, I guess you are right but we
1950 #warning should verify when this is needed
1952 // Jürgen, would you like to have a look?
1953 // I guess we need to move the outer cursor
1954 // and open and lock the inset (bla bla bla)
1955 // stuff I don't know... so can you have a look?
1957 // I moved the lyxerr stuff in here so we can see if
1958 // this is actually really needed and where!
1960 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
1965 setCursor(bview, cursor, par, pos, boundary);
1967 setCurrentFont(bview);
1971 void LyXText::setCurrentFont(BufferView * bview) const
1973 pos_type pos = cursor.pos();
1974 if (cursor.boundary() && pos > 0)
1978 if (pos == cursor.par()->size())
1980 else // potentional bug... BUG (Lgb)
1981 if (cursor.par()->isSeparator(pos)) {
1982 if (pos > cursor.row()->pos() &&
1983 bidi_level(pos) % 2 ==
1984 bidi_level(pos - 1) % 2)
1986 else if (pos + 1 < cursor.par()->size())
1992 cursor.par()->getFontSettings(bview->buffer()->params, pos);
1993 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
1995 if (cursor.pos() == cursor.par()->size() &&
1996 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
1997 !cursor.boundary()) {
1998 Language const * lang =
1999 cursor.par()->getParLanguage(bview->buffer()->params);
2000 current_font.setLanguage(lang);
2001 current_font.setNumber(LyXFont::OFF);
2002 real_current_font.setLanguage(lang);
2003 real_current_font.setNumber(LyXFont::OFF);
2008 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2010 LyXCursor old_cursor = cursor;
2012 setCursorFromCoordinates(bview, cursor, x, y);
2013 setCurrentFont(bview);
2014 deleteEmptyParagraphMechanism(bview, old_cursor);
2021 * return true if the cursor given is at the end of a row,
2022 * and the next row is filled by an inset that spans an entire
2025 bool beforeFullRowInset(Row & row, LyXCursor & cur) {
2028 Row const & next = *row.next();
2030 if (next.pos() != cur.pos() || next.par() != cur.par())
2032 if (!cur.par()->isInset(cur.pos()))
2034 Inset const * inset = cur.par()->getInset(cur.pos());
2035 if (inset->needFullRow() || inset->display())
2042 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2045 // Get the row first.
2047 Row * row = getRowNearY(y);
2049 pos_type const column = getColumnNearX(bview, row, x, bound);
2050 cur.par(row->par());
2051 cur.pos(row->pos() + column);
2053 cur.y(y + row->baseline());
2056 if (beforeFullRowInset(*row, cur)) {
2057 pos_type last = rowLastPrintable(row);
2058 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2060 cur.iy(y + row->height() + row->next()->baseline());
2061 cur.irow(row->next());
2067 cur.boundary(bound);
2071 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2073 if (cursor.pos() > 0) {
2074 bool boundary = cursor.boundary();
2075 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2076 if (!internal && !boundary &&
2077 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2078 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2079 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2080 Paragraph * par = cursor.par()->previous();
2081 setCursor(bview, par, par->size());
2086 void LyXText::cursorRight(BufferView * bview, bool internal) const
2088 if (!internal && cursor.boundary() &&
2089 !cursor.par()->isNewline(cursor.pos()))
2090 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2091 else if (cursor.pos() < cursor.par()->size()) {
2092 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2094 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2095 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2096 } else if (cursor.par()->next())
2097 setCursor(bview, cursor.par()->next(), 0);
2101 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2104 int x = cursor.x_fix();
2105 int y = cursor.y() - cursor.row()->baseline() - 1;
2106 setCursorFromCoordinates(bview, x, y);
2108 int y1 = cursor.iy() - first_y;
2112 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2113 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2114 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2118 setCursorFromCoordinates(bview, cursor.x_fix(),
2119 cursor.y() - cursor.row()->baseline() - 1);
2124 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2127 int x = cursor.x_fix();
2128 int y = cursor.y() - cursor.row()->baseline() +
2129 cursor.row()->height() + 1;
2130 setCursorFromCoordinates(bview, x, y);
2131 if (!selecting && cursor.row() == cursor.irow()) {
2132 int y1 = cursor.iy() - first_y;
2136 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2137 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2138 inset_hit->edit(bview, x, y - (y2 - y1), mouse_button::none);
2142 setCursorFromCoordinates(bview, cursor.x_fix(),
2143 cursor.y() - cursor.row()->baseline()
2144 + cursor.row()->height() + 1);
2149 void LyXText::cursorUpParagraph(BufferView * bview) const
2151 if (cursor.pos() > 0) {
2152 setCursor(bview, cursor.par(), 0);
2154 else if (cursor.par()->previous()) {
2155 setCursor(bview, cursor.par()->previous(), 0);
2160 void LyXText::cursorDownParagraph(BufferView * bview) const
2162 if (cursor.par()->next()) {
2163 setCursor(bview, cursor.par()->next(), 0);
2165 setCursor(bview, cursor.par(), cursor.par()->size());
2169 // fix the cursor `cur' after a characters has been deleted at `where'
2170 // position. Called by deleteEmptyParagraphMechanism
2171 void LyXText::fixCursorAfterDelete(BufferView * bview,
2173 LyXCursor const & where) const
2175 // if cursor is not in the paragraph where the delete occured,
2177 if (cur.par() != where.par())
2180 // if cursor position is after the place where the delete occured,
2182 if (cur.pos() > where.pos())
2183 cur.pos(cur.pos()-1);
2185 // check also if we don't want to set the cursor on a spot behind the
2186 // pagragraph because we erased the last character.
2187 if (cur.pos() > cur.par()->size())
2188 cur.pos(cur.par()->size());
2190 // recompute row et al. for this cursor
2191 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2195 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2196 LyXCursor const & old_cursor) const
2198 // Would be wrong to delete anything if we have a selection.
2199 if (selection.set())
2202 // We allow all kinds of "mumbo-jumbo" when freespacing.
2203 if (old_cursor.par()->layout()->free_spacing
2204 || old_cursor.par()->isFreeSpacing()) {
2208 /* Ok I'll put some comments here about what is missing.
2209 I have fixed BackSpace (and thus Delete) to not delete
2210 double-spaces automagically. I have also changed Cut,
2211 Copy and Paste to hopefully do some sensible things.
2212 There are still some small problems that can lead to
2213 double spaces stored in the document file or space at
2214 the beginning of paragraphs. This happens if you have
2215 the cursor betwenn to spaces and then save. Or if you
2216 cut and paste and the selection have a space at the
2217 beginning and then save right after the paste. I am
2218 sure none of these are very hard to fix, but I will
2219 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2220 that I can get some feedback. (Lgb)
2223 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2224 // delete the LineSeparator.
2227 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2228 // delete the LineSeparator.
2231 // If the pos around the old_cursor were spaces, delete one of them.
2232 if (old_cursor.par() != cursor.par()
2233 || old_cursor.pos() != cursor.pos()) {
2234 // Only if the cursor has really moved
2236 if (old_cursor.pos() > 0
2237 && old_cursor.pos() < old_cursor.par()->size()
2238 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2239 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2240 old_cursor.par()->erase(old_cursor.pos() - 1);
2241 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2243 #ifdef WITH_WARNINGS
2244 #warning This will not work anymore when we have multiple views of the same buffer
2245 // In this case, we will have to correct also the cursors held by
2246 // other bufferviews. It will probably be easier to do that in a more
2247 // automated way in LyXCursor code. (JMarc 26/09/2001)
2249 // correct all cursors held by the LyXText
2250 fixCursorAfterDelete(bview, cursor, old_cursor);
2251 fixCursorAfterDelete(bview, selection.cursor,
2253 fixCursorAfterDelete(bview, selection.start,
2255 fixCursorAfterDelete(bview, selection.end, old_cursor);
2256 fixCursorAfterDelete(bview, last_sel_cursor,
2258 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2259 fixCursorAfterDelete(bview, toggle_end_cursor,
2265 // don't delete anything if this is the ONLY paragraph!
2266 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2269 // Do not delete empty paragraphs with keepempty set.
2270 if (old_cursor.par()->layout()->keepempty)
2273 // only do our magic if we changed paragraph
2274 if (old_cursor.par() == cursor.par())
2277 // record if we have deleted a paragraph
2278 // we can't possibly have deleted a paragraph before this point
2279 bool deleted = false;
2281 if ((old_cursor.par()->empty()
2282 || (old_cursor.par()->size() == 1
2283 && old_cursor.par()->isLineSeparator(0)))) {
2284 // ok, we will delete anything
2285 LyXCursor tmpcursor;
2287 // make sure that you do not delete any environments
2288 status(bview, LyXText::NEED_MORE_REFRESH);
2291 if (old_cursor.row()->previous()) {
2292 refresh_row = old_cursor.row()->previous();
2293 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2295 cursor = old_cursor; // that undo can restore the right cursor position
2296 Paragraph * endpar = old_cursor.par()->next();
2297 if (endpar && endpar->getDepth()) {
2298 while (endpar && endpar->getDepth()) {
2299 endpar = endpar->next();
2302 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2306 removeRow(old_cursor.row());
2307 if (ownerParagraph() == old_cursor.par()) {
2308 ownerParagraph(ownerParagraph()->next());
2311 delete old_cursor.par();
2313 /* Breakagain the next par. Needed because of
2314 * the parindent that can occur or dissappear.
2315 * The next row can change its height, if
2316 * there is another layout before */
2317 if (refresh_row->next()) {
2318 breakAgain(bview, refresh_row->next());
2319 updateCounters(bview);
2321 setHeightOfRow(bview, refresh_row);
2323 refresh_row = old_cursor.row()->next();
2324 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2327 cursor = old_cursor; // that undo can restore the right cursor position
2328 Paragraph * endpar = old_cursor.par()->next();
2329 if (endpar && endpar->getDepth()) {
2330 while (endpar && endpar->getDepth()) {
2331 endpar = endpar->next();
2334 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2338 removeRow(old_cursor.row());
2340 if (ownerParagraph() == old_cursor.par()) {
2341 ownerParagraph(ownerParagraph()->next());
2344 delete old_cursor.par();
2346 /* Breakagain the next par. Needed because of
2347 the parindent that can occur or dissappear.
2348 The next row can change its height, if
2349 there is another layout before */
2351 breakAgain(bview, refresh_row);
2352 updateCounters(bview);
2357 setCursorIntern(bview, cursor.par(), cursor.pos());
2359 if (selection.cursor.par() == old_cursor.par()
2360 && selection.cursor.pos() == old_cursor.pos()) {
2361 // correct selection
2362 selection.cursor = cursor;
2366 if (old_cursor.par()->stripLeadingSpaces()) {
2367 redoParagraphs(bview, old_cursor,
2368 old_cursor.par()->next());
2370 setCursorIntern(bview, cursor.par(), cursor.pos());
2371 selection.cursor = cursor;
2378 Paragraph * LyXText::ownerParagraph() const
2381 return inset_owner->paragraph();
2383 return &*(bv_owner->buffer()->paragraphs.begin());
2387 void LyXText::ownerParagraph(Paragraph * p) const
2390 inset_owner->paragraph(p);
2392 bv_owner->buffer()->paragraphs.set(p);
2397 void LyXText::ownerParagraph(int id, Paragraph * p) const
2399 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2400 if (op && op->inInset()) {
2401 static_cast<InsetText *>(op->inInset())->paragraph(p);
2408 LyXText::text_status LyXText::status() const
2414 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2416 LyXText * t = bview->text;
2418 // We should only go up with refreshing code so this means that if
2419 // we have a MORE refresh we should never set it to LITTLE if we still
2420 // didn't handle it (and then it will be UNCHANGED. Now as long as
2421 // we stay inside one LyXText this may work but we need to tell the
2422 // outermost LyXText that it should REALLY draw us if there is some
2423 // change in a Inset::LyXText. So you see that when we are inside a
2424 // inset's LyXText we give the LITTLE to the outermost LyXText to
2425 // tell'em that it should redraw the actual row (where the inset
2426 // resides! Capito?!
2428 if (status_ != NEED_MORE_REFRESH || st != NEED_VERY_LITTLE_REFRESH) {
2430 if (inset_owner && st != UNCHANGED) {
2431 t->status(bview, NEED_VERY_LITTLE_REFRESH);
2432 if (!t->refresh_row) {
2433 t->refresh_row = t->cursor.row();
2434 t->refresh_y = t->cursor.y() -
2435 t->cursor.row()->baseline();