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 "lyxtextclasslist.h"
22 #include "undo_funcs.h"
24 #include "bufferparams.h"
26 #include "BufferView.h"
28 #include "CutAndPaste.h"
34 #include "FloatList.h"
36 #include "ParagraphParameters.h"
38 #include "insets/inseterror.h"
39 #include "insets/insetbib.h"
40 #include "insets/insetspecialchar.h"
41 #include "insets/insettext.h"
42 #include "insets/insetfloat.h"
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first_y(0),
59 bv_owner(bv), inset_owner(0), the_locking_inset(0),
60 need_break_row(0), refresh_y(0), refresh_row(0),
61 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
65 LyXText::LyXText(InsetText * inset)
66 : number_of_rows(0), height(0), width(0), first_y(0),
67 bv_owner(0), inset_owner(inset), the_locking_inset(0),
68 need_break_row(0), refresh_y(0), refresh_row(0),
69 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
73 void LyXText::init(BufferView * bview, bool reinit)
76 // Delete all rows, this does not touch the paragraphs!
77 Row * tmprow = firstrow;
79 tmprow = firstrow->next();
88 copylayouttype.erase();
89 number_of_rows = first_y = refresh_y = 0;
90 status_ = LyXText::UNCHANGED;
94 Paragraph * par = ownerParagraph();
95 current_font = getFont(bview->buffer(), par, 0);
98 insertParagraph(bview, par, lastrow);
101 setCursorIntern(bview, firstrow->par(), 0);
102 selection.cursor = cursor;
108 // Delete all rows, this does not touch the paragraphs!
109 Row * tmprow = firstrow;
111 tmprow = firstrow->next();
120 LyXFont const realizeFont(LyXFont const & font,
124 LyXFont tmpfont(font);
125 Paragraph::depth_type par_depth = par->getDepth();
127 // Resolve against environment font information
128 while (par && par_depth && !tmpfont.resolved()) {
129 par = par->outerHook();
131 #ifndef INHERIT_LANGUAGE
132 tmpfont.realize(textclasslist[buf->params.textclass][
133 par->layout()].font);
135 tmpfont.realize(textclasslist.
136 Style(buf->params.textclass,
138 buf->params.language);
140 par_depth = par->getDepth();
144 #ifndef INHERIT_LANGUAGE
145 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
147 tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
148 buf->params.language);
157 // Gets the fully instantiated font at a given position in a paragraph
158 // Basically the same routine as Paragraph::getFont() in paragraph.C.
159 // The difference is that this one is used for displaying, and thus we
160 // are allowed to make cosmetic improvements. For instance make footnotes
162 // If position is -1, we get the layout font of the paragraph.
163 // If position is -2, we get the font of the manual label of the paragraph.
164 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
167 lyx::Assert(pos >= 0);
169 LyXLayout const & layout =
170 textclasslist[buf->params.textclass][par->layout()];
172 Paragraph::depth_type par_depth = par->getDepth();
173 // We specialize the 95% common case:
175 if (layout.labeltype == LABEL_MANUAL
176 && pos < beginningOfMainBody(buf, par)) {
178 LyXFont f = par->getFontSettings(buf->params, pos);
180 par->inInset()->getDrawFont(f);
181 #ifndef INHERIT_LANGUAGE
182 return f.realize(layout.reslabelfont);
184 return f.realize(layout.reslabelfont, buf->params.language);
187 LyXFont f = par->getFontSettings(buf->params, pos);
189 par->inInset()->getDrawFont(f);
190 #ifndef INHERIT_LANGUAGE
191 return f.realize(layout.resfont);
193 return f.realize(layout.resfont, buf->params.language);
198 // The uncommon case need not be optimized as much
202 if (pos < beginningOfMainBody(buf, par)) {
204 layoutfont = layout.labelfont;
207 layoutfont = layout.font;
210 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
211 #ifndef INHERIT_LANGUAGE
212 tmpfont.realize(layoutfont);
214 tmpfont.realize(layoutfont, buf->params.language);
217 par->inInset()->getDrawFont(tmpfont);
219 return realizeFont(tmpfont, buf, par);
223 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
225 LyXLayout const & layout =
226 textclasslist[buf->params.textclass][par->layout()];
228 Paragraph::depth_type par_depth = par->getDepth();
231 return layout.resfont;
234 return realizeFont(layout.font, buf, par);
238 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
240 LyXLayout const & layout =
241 textclasslist[buf->params.textclass][par->layout()];
243 Paragraph::depth_type par_depth = par->getDepth();
246 return layout.reslabelfont;
249 return realizeFont(layout.labelfont, buf, par);
253 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
254 pos_type pos, LyXFont const & fnt,
257 Buffer const * buf = bv->buffer();
258 LyXFont font = getFont(buf, par, pos);
259 font.update(fnt, buf->params.language, toggleall);
260 // Let the insets convert their font
261 if (par->isInset(pos)) {
262 Inset * inset = par->getInset(pos);
263 if (isEditableInset(inset)) {
264 UpdatableInset * uinset =
265 static_cast<UpdatableInset *>(inset);
266 uinset->setFont(bv, fnt, toggleall, true);
270 // Plug thru to version below:
271 setCharFont(buf, par, pos, font);
275 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
276 pos_type pos, LyXFont const & fnt)
280 LyXTextClass const & tclass = textclasslist[buf->params.textclass];
281 LyXLayout const & layout = tclass[par->layout()];
283 // Get concrete layout font to reduce against
286 if (pos < beginningOfMainBody(buf, par))
287 layoutfont = layout.labelfont;
289 layoutfont = layout.font;
291 // Realize against environment font information
292 if (par->getDepth()) {
293 Paragraph * tp = par;
294 while (!layoutfont.resolved() && tp && tp->getDepth()) {
295 tp = tp->outerHook();
297 #ifndef INHERIT_LANGUAGE
298 layoutfont.realize(tclass[tp->layout()].font);
300 layoutfont.realize(textclasslist.
301 Style(buf->params.textclass,
303 buf->params.language);
308 #ifndef INHERIT_LANGUAGE
309 layoutfont.realize(tclass.defaultfont());
311 layoutfont.realize(tclass.defaultfont(), buf->params.language);
314 // Now, reduce font against full layout font
315 font.reduce(layoutfont);
317 par->setFont(pos, font);
321 // inserts a new row behind the specified row, increments
322 // the touched counters
323 void LyXText::insertRow(Row * row, Paragraph * par,
326 Row * tmprow = new Row;
329 tmprow->next(firstrow);
332 tmprow->previous(row);
333 tmprow->next(row->next());
338 tmprow->next()->previous(tmprow);
340 if (tmprow->previous())
341 tmprow->previous()->next(tmprow);
353 // removes the row and reset the touched counters
354 void LyXText::removeRow(Row * row) const
356 Row * row_prev = row->previous();
358 row->next()->previous(row_prev);
360 firstrow = row->next();
361 // lyx::Assert(firstrow);
363 row_prev->next(row->next());
365 if (row == lastrow) {
366 lyx::Assert(!row->next());
369 if (refresh_row == row) {
370 refresh_row = row_prev ? row_prev : row->next();
371 // what about refresh_y, refresh_height
374 height -= row->height(); // the text becomes smaller
377 --number_of_rows; // one row less
381 // remove all following rows of the paragraph of the specified row.
382 void LyXText::removeParagraph(Row * row) const
384 Paragraph * tmppar = row->par();
388 while (row && row->par() == tmppar) {
389 tmprow = row->next();
396 // insert the specified paragraph behind the specified row
397 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
400 insertRow(row, par, 0); /* insert a new row, starting
403 setCounter(bview->buffer(), par); // set the counters
405 // and now append the whole paragraph behind the new row
408 appendParagraph(bview, firstrow);
410 row->next()->height(0);
411 appendParagraph(bview, row->next());
416 Inset * LyXText::getInset() const
419 if (cursor.pos() == 0 && cursor.par()->bibkey) {
420 inset = cursor.par()->bibkey;
421 } else if (cursor.pos() < cursor.par()->size()
422 && cursor.par()->isInset(cursor.pos())) {
423 inset = cursor.par()->getInset(cursor.pos());
429 void LyXText::toggleInset(BufferView * bview)
431 Inset * inset = getInset();
432 // is there an editable inset at cursor position?
433 if (!isEditableInset(inset)) {
434 // No, try to see if we are inside a collapsable inset
435 if (inset_owner && inset_owner->owner()
436 && inset_owner->owner()->isOpen()) {
437 bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
438 inset_owner->owner()->close(bview);
442 //bview->owner()->message(inset->editMessage());
444 // do we want to keep this?? (JMarc)
445 if (!isHighlyEditableInset(inset))
446 setCursorParUndo(bview);
448 if (inset->isOpen()) {
454 inset->open(bview, !inset->isOpen());
459 /* used in setlayout */
460 // Asger is not sure we want to do this...
461 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
464 LyXLayout const & layout =
465 textclasslist[buf->params.textclass][par->layout()];
468 for (pos_type pos = 0; pos < par->size(); ++pos) {
469 if (pos < beginningOfMainBody(buf, par))
470 layoutfont = layout.labelfont;
472 layoutfont = layout.font;
474 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
475 tmpfont.reduce(layoutfont);
476 par->setFont(pos, tmpfont);
481 Paragraph * LyXText::setLayout(BufferView * bview,
482 LyXCursor & cur, LyXCursor & sstart_cur,
483 LyXCursor & send_cur,
484 string const & layout)
486 Paragraph * endpar = send_cur.par()->next();
487 Paragraph * undoendpar = endpar;
489 if (endpar && endpar->getDepth()) {
490 while (endpar && endpar->getDepth()) {
491 endpar = endpar->next();
495 endpar = endpar->next(); // because of parindents etc.
498 setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
500 // ok we have a selection. This is always between sstart_cur
501 // and sel_end cursor
503 Paragraph * par = sstart_cur.par();
504 Paragraph * epar = send_cur.par()->next();
506 LyXLayout const & lyxlayout =
507 textclasslist[bview->buffer()->params.textclass][layout];
510 par->applyLayout(layout);
511 makeFontEntriesLayoutSpecific(bview->buffer(), par);
512 Paragraph * fppar = par;
513 fppar->params().spaceTop(lyxlayout.fill_top ?
514 VSpace(VSpace::VFILL)
515 : VSpace(VSpace::NONE));
516 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
517 VSpace(VSpace::VFILL)
518 : VSpace(VSpace::NONE));
519 if (lyxlayout.margintype == MARGIN_MANUAL)
520 par->setLabelWidthString(lyxlayout.labelstring());
521 if (lyxlayout.labeltype != LABEL_BIBLIO
523 delete fppar->bibkey;
528 } while (par != epar);
534 // set layout over selection and make a total rebreak of those paragraphs
535 void LyXText::setLayout(BufferView * bview, string const & layout)
537 LyXCursor tmpcursor = cursor; /* store the current cursor */
539 // if there is no selection just set the layout
540 // of the current paragraph */
541 if (!selection.set()) {
542 selection.start = cursor; // dummy selection
543 selection.end = cursor;
545 Paragraph * endpar = setLayout(bview, cursor, selection.start,
546 selection.end, layout);
547 redoParagraphs(bview, selection.start, endpar);
549 // we have to reset the selection, because the
550 // geometry could have changed
551 setCursor(bview, selection.start.par(),
552 selection.start.pos(), false);
553 selection.cursor = cursor;
554 setCursor(bview, selection.end.par(), selection.end.pos(), false);
555 updateCounters(bview, cursor.row());
558 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
562 // increment depth over selection and
563 // make a total rebreak of those paragraphs
564 void LyXText::incDepth(BufferView * bview)
566 // If there is no selection, just use the current paragraph
567 if (!selection.set()) {
568 selection.start = cursor; // dummy selection
569 selection.end = cursor;
572 // We end at the next paragraph with depth 0
573 Paragraph * endpar = selection.end.par()->next();
575 Paragraph * undoendpar = endpar;
577 if (endpar && endpar->getDepth()) {
578 while (endpar && endpar->getDepth()) {
579 endpar = endpar->next();
583 endpar = endpar->next(); // because of parindents etc.
586 setUndo(bview, Undo::EDIT,
587 selection.start.par(), undoendpar);
589 LyXCursor tmpcursor = cursor; // store the current cursor
591 // ok we have a selection. This is always between sel_start_cursor
592 // and sel_end cursor
593 cursor = selection.start;
595 bool anything_changed = false;
597 LyXTextClass const & tclass =
598 textclasslist[bview->buffer()->params.textclass];
601 // NOTE: you can't change the depth of a bibliography entry
602 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
603 Paragraph * prev = cursor.par()->previous();
606 if (cursor.par()->getDepth()
607 < prev->getMaxDepthAfter(bview->buffer())){
608 cursor.par()->params().depth(cursor.par()->getDepth() + 1);
609 anything_changed = true;
613 if (cursor.par() == selection.end.par())
615 cursor.par(cursor.par()->next());
618 // if nothing changed set all depth to 0
619 if (!anything_changed) {
620 cursor = selection.start;
621 while (cursor.par() != selection.end.par()) {
622 cursor.par()->params().depth(0);
623 cursor.par(cursor.par()->next());
625 cursor.par()->params().depth(0);
628 redoParagraphs(bview, selection.start, endpar);
630 // we have to reset the selection, because the
631 // geometry could have changed
632 setCursor(bview, selection.start.par(), selection.start.pos());
633 selection.cursor = cursor;
634 setCursor(bview, selection.end.par(), selection.end.pos());
635 updateCounters(bview, cursor.row());
638 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
642 // decrement depth over selection and
643 // make a total rebreak of those paragraphs
644 void LyXText::decDepth(BufferView * bview)
646 // if there is no selection just set the layout
647 // of the current paragraph
648 if (!selection.set()) {
649 selection.start = cursor; // dummy selection
650 selection.end = cursor;
652 Paragraph * endpar = selection.end.par()->next();
653 Paragraph * undoendpar = endpar;
655 if (endpar && endpar->getDepth()) {
656 while (endpar && endpar->getDepth()) {
657 endpar = endpar->next();
661 endpar = endpar->next(); // because of parindents etc.
664 setUndo(bview, Undo::EDIT,
665 selection.start.par(), undoendpar);
667 LyXCursor tmpcursor = cursor; // store the current cursor
669 // ok we have a selection. This is always between sel_start_cursor
670 // and sel_end cursor
671 cursor = selection.start;
674 if (cursor.par()->params().depth()) {
675 cursor.par()->params()
676 .depth(cursor.par()->params().depth() - 1);
678 if (cursor.par() == selection.end.par()) {
681 cursor.par(cursor.par()->next());
684 redoParagraphs(bview, selection.start, endpar);
686 // we have to reset the selection, because the
687 // geometry could have changed
688 setCursor(bview, selection.start.par(),
689 selection.start.pos());
690 selection.cursor = cursor;
691 setCursor(bview, selection.end.par(), selection.end.pos());
692 updateCounters(bview, cursor.row());
695 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
699 // set font over selection and make a total rebreak of those paragraphs
700 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
702 // if there is no selection just set the current_font
703 if (!selection.set()) {
704 // Determine basis font
706 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
708 layoutfont = getLabelFont(bview->buffer(),
711 layoutfont = getLayoutFont(bview->buffer(),
714 // Update current font
715 real_current_font.update(font,
716 bview->buffer()->params.language,
719 // Reduce to implicit settings
720 current_font = real_current_font;
721 current_font.reduce(layoutfont);
722 // And resolve it completely
723 #ifndef INHERIT_LANGUAGE
724 real_current_font.realize(layoutfont);
726 real_current_font.realize(layoutfont,
727 bview->buffer()->params.language);
732 LyXCursor tmpcursor = cursor; // store the current cursor
734 // ok we have a selection. This is always between sel_start_cursor
735 // and sel_end cursor
737 setUndo(bview, Undo::EDIT,
738 selection.start.par(), selection.end.par()->next());
740 cursor = selection.start;
741 while (cursor.par() != selection.end.par() ||
742 (cursor.pos() < selection.end.pos()))
744 if (cursor.pos() < cursor.par()->size()) {
745 // an open footnote should behave
747 setCharFont(bview, cursor.par(), cursor.pos(),
749 cursor.pos(cursor.pos() + 1);
752 cursor.par(cursor.par()->next());
757 redoParagraphs(bview, selection.start, selection.end.par()->next());
759 // we have to reset the selection, because the
760 // geometry could have changed, but we keep
761 // it for user convenience
762 setCursor(bview, selection.start.par(), selection.start.pos());
763 selection.cursor = cursor;
764 setCursor(bview, selection.end.par(), selection.end.pos());
766 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
767 tmpcursor.boundary());
771 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
773 Row * tmprow = cur.row();
774 int y = cur.y() - tmprow->baseline();
776 setHeightOfRow(bview, tmprow);
778 while (tmprow->previous()
779 && tmprow->previous()->par() == tmprow->par()) {
780 tmprow = tmprow->previous();
781 y -= tmprow->height();
782 setHeightOfRow(bview, tmprow);
785 // we can set the refreshing parameters now
786 status(bview, LyXText::NEED_MORE_REFRESH);
788 refresh_row = tmprow;
789 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
793 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
795 Row * tmprow = cur.row();
797 int y = cur.y() - tmprow->baseline();
798 setHeightOfRow(bview, tmprow);
800 while (tmprow->previous()
801 && tmprow->previous()->par() == tmprow->par()) {
802 tmprow = tmprow->previous();
803 y -= tmprow->height();
806 // we can set the refreshing parameters now
807 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
809 refresh_row = tmprow;
811 status(bview, LyXText::NEED_MORE_REFRESH);
812 setCursor(bview, cur.par(), cur.pos());
816 // deletes and inserts again all paragaphs between the cursor
817 // and the specified par
818 // This function is needed after SetLayout and SetFont etc.
819 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
820 Paragraph const * endpar) const
823 Paragraph * tmppar = 0;
824 Paragraph * first_phys_par = 0;
826 Row * tmprow = cur.row();
828 int y = cur.y() - tmprow->baseline();
830 if (!tmprow->previous()) {
831 // a trick/hack for UNDO
832 // This is needed because in an UNDO/REDO we could have changed
833 // the ownerParagrah() so the paragraph inside the row is NOT
834 // my really first par anymore. Got it Lars ;) (Jug 20011206)
835 first_phys_par = ownerParagraph();
837 first_phys_par = tmprow->par();
838 while (tmprow->previous()
839 && tmprow->previous()->par() == first_phys_par)
841 tmprow = tmprow->previous();
842 y -= tmprow->height();
846 // we can set the refreshing parameters now
847 status(bview, LyXText::NEED_MORE_REFRESH);
849 refresh_row = tmprow->previous(); /* the real refresh row will
850 be deleted, so I store
854 tmppar = tmprow->next()->par();
857 while (tmprow->next() && tmppar != endpar) {
858 removeRow(tmprow->next());
859 if (tmprow->next()) {
860 tmppar = tmprow->next()->par();
866 // remove the first one
867 tmprow2 = tmprow; /* this is because tmprow->previous()
869 tmprow = tmprow->previous();
872 tmppar = first_phys_par;
876 insertParagraph(bview, tmppar, tmprow);
880 while (tmprow->next()
881 && tmprow->next()->par() == tmppar) {
882 tmprow = tmprow->next();
884 tmppar = tmppar->next();
886 } while (tmppar && tmppar != endpar);
888 // this is because of layout changes
890 refresh_y -= refresh_row->height();
891 setHeightOfRow(bview, refresh_row);
893 refresh_row = firstrow;
895 setHeightOfRow(bview, refresh_row);
898 if (tmprow && tmprow->next())
899 setHeightOfRow(bview, tmprow->next());
903 bool LyXText::fullRebreak(BufferView * bview)
909 if (need_break_row) {
910 breakAgain(bview, need_break_row);
918 // important for the screen
921 /* the cursor set functions have a special mechanism. When they
922 * realize, that you left an empty paragraph, they will delete it.
923 * They also delete the corresponding row */
925 // need the selection cursor:
926 void LyXText::setSelection(BufferView * bview)
928 bool const lsel = selection.set();
930 if (!selection.set()) {
931 last_sel_cursor = selection.cursor;
932 selection.start = selection.cursor;
933 selection.end = selection.cursor;
938 // first the toggling area
939 if (cursor.y() < last_sel_cursor.y()
940 || (cursor.y() == last_sel_cursor.y()
941 && cursor.x() < last_sel_cursor.x())) {
942 toggle_end_cursor = last_sel_cursor;
943 toggle_cursor = cursor;
945 toggle_end_cursor = cursor;
946 toggle_cursor = last_sel_cursor;
949 last_sel_cursor = cursor;
951 // and now the whole selection
953 if (selection.cursor.par() == cursor.par())
954 if (selection.cursor.pos() < cursor.pos()) {
955 selection.end = cursor;
956 selection.start = selection.cursor;
958 selection.end = selection.cursor;
959 selection.start = cursor;
961 else if (selection.cursor.y() < cursor.y() ||
962 (selection.cursor.y() == cursor.y()
963 && selection.cursor.x() < cursor.x())) {
964 selection.end = cursor;
965 selection.start = selection.cursor;
968 selection.end = selection.cursor;
969 selection.start = cursor;
972 // a selection with no contents is not a selection
973 if (selection.start.par() == selection.end.par() &&
974 selection.start.pos() == selection.end.pos())
975 selection.set(false);
977 if (inset_owner && (selection.set() || lsel))
978 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
982 string const LyXText::selectionAsString(Buffer const * buffer,
985 if (!selection.set()) return string();
988 // Special handling if the whole selection is within one paragraph
989 if (selection.start.par() == selection.end.par()) {
990 result += selection.start.par()->asString(buffer,
991 selection.start.pos(),
997 // The selection spans more than one paragraph
999 // First paragraph in selection
1000 result += selection.start.par()->asString(buffer,
1001 selection.start.pos(),
1002 selection.start.par()->size(),
1006 // The paragraphs in between (if any)
1007 LyXCursor tmpcur(selection.start);
1008 tmpcur.par(tmpcur.par()->next());
1009 while (tmpcur.par() != selection.end.par()) {
1010 result += tmpcur.par()->asString(buffer, 0,
1011 tmpcur.par()->size(),
1013 tmpcur.par(tmpcur.par()->next());
1016 // Last paragraph in selection
1017 result += selection.end.par()->asString(buffer, 0,
1018 selection.end.pos(), label);
1024 void LyXText::clearSelection() const
1026 selection.set(false);
1027 selection.mark(false);
1028 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1029 // reset this in the bv_owner!
1030 if (bv_owner && bv_owner->text)
1031 bv_owner->text->xsel_cache.set(false);
1035 void LyXText::cursorHome(BufferView * bview) const
1037 setCursor(bview, cursor.par(), cursor.row()->pos());
1041 void LyXText::cursorEnd(BufferView * bview) const
1043 if (!cursor.row()->next()
1044 || cursor.row()->next()->par() != cursor.row()->par()) {
1045 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1047 if (cursor.par()->size() &&
1048 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1049 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1050 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1052 setCursor(bview,cursor.par(),
1053 rowLast(cursor.row()) + 1);
1059 void LyXText::cursorTop(BufferView * bview) const
1061 while (cursor.par()->previous())
1062 cursor.par(cursor.par()->previous());
1063 setCursor(bview, cursor.par(), 0);
1067 void LyXText::cursorBottom(BufferView * bview) const
1069 while (cursor.par()->next())
1070 cursor.par(cursor.par()->next());
1071 setCursor(bview, cursor.par(), cursor.par()->size());
1075 void LyXText::toggleFree(BufferView * bview,
1076 LyXFont const & font, bool toggleall)
1078 // If the mask is completely neutral, tell user
1079 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1080 // Could only happen with user style
1081 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1085 // Try implicit word selection
1086 // If there is a change in the language the implicit word selection
1088 LyXCursor resetCursor = cursor;
1089 bool implicitSelection = (font.language() == ignore_language
1090 && font.number() == LyXFont::IGNORE)
1091 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1094 setFont(bview, font, toggleall);
1096 // Implicit selections are cleared afterwards
1097 //and cursor is set to the original position.
1098 if (implicitSelection) {
1100 cursor = resetCursor;
1101 setCursor(bview, cursor.par(), cursor.pos());
1102 selection.cursor = cursor;
1105 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1109 string LyXText::getStringToIndex(BufferView * bview)
1113 // Try implicit word selection
1114 // If there is a change in the language the implicit word selection
1116 LyXCursor resetCursor = cursor;
1117 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1119 if (!selection.set()) {
1120 bview->owner()->message(_("Nothing to index!"));
1123 if (selection.start.par() != selection.end.par()) {
1124 bview->owner()->message(_("Cannot index more than one paragraph!"));
1128 idxstring = selectionAsString(bview->buffer(), false);
1130 // Implicit selections are cleared afterwards
1131 //and cursor is set to the original position.
1132 if (implicitSelection) {
1134 cursor = resetCursor;
1135 setCursor(bview, cursor.par(), cursor.pos());
1136 selection.cursor = cursor;
1142 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1143 Paragraph const * par) const
1145 if (textclasslist[buf->params.textclass][
1146 par->layout()].labeltype != LABEL_MANUAL)
1149 return par->beginningOfMainBody();
1153 /* the DTP switches for paragraphs. LyX will store them in the
1154 * first physicla paragraph. When a paragraph is broken, the top settings
1155 * rest, the bottom settings are given to the new one. So I can make shure,
1156 * they do not duplicate themself and you cannnot make dirty things with
1159 void LyXText::setParagraph(BufferView * bview,
1160 bool line_top, bool line_bottom,
1161 bool pagebreak_top, bool pagebreak_bottom,
1162 VSpace const & space_top,
1163 VSpace const & space_bottom,
1164 Spacing const & spacing,
1166 string labelwidthstring,
1169 LyXCursor tmpcursor = cursor;
1170 if (!selection.set()) {
1171 selection.start = cursor;
1172 selection.end = cursor;
1175 // make sure that the depth behind the selection are restored, too
1176 Paragraph * endpar = selection.end.par()->next();
1177 Paragraph * undoendpar = endpar;
1179 if (endpar && endpar->getDepth()) {
1180 while (endpar && endpar->getDepth()) {
1181 endpar = endpar->next();
1182 undoendpar = endpar;
1186 // because of parindents etc.
1187 endpar = endpar->next();
1190 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1193 Paragraph * tmppar = selection.end.par();
1194 LyXTextClass const & tclass =
1195 textclasslist[bview->buffer()->params.textclass];
1197 while (tmppar != selection.start.par()->previous()) {
1198 setCursor(bview, tmppar, 0);
1199 status(bview, LyXText::NEED_MORE_REFRESH);
1200 refresh_row = cursor.row();
1201 refresh_y = cursor.y() - cursor.row()->baseline();
1202 cursor.par()->params().lineTop(line_top);
1203 cursor.par()->params().lineBottom(line_bottom);
1204 cursor.par()->params().pagebreakTop(pagebreak_top);
1205 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1206 cursor.par()->params().spaceTop(space_top);
1207 cursor.par()->params().spaceBottom(space_bottom);
1208 cursor.par()->params().spacing(spacing);
1209 // does the layout allow the new alignment?
1210 LyXLayout const & layout = tclass[cursor.par()->layout()];
1212 if (align == LYX_ALIGN_LAYOUT)
1213 align = layout.align;
1214 if (align & layout.alignpossible) {
1215 if (align == layout.align)
1216 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1218 cursor.par()->params().align(align);
1220 cursor.par()->setLabelWidthString(labelwidthstring);
1221 cursor.par()->params().noindent(noindent);
1222 tmppar = cursor.par()->previous();
1225 redoParagraphs(bview, selection.start, endpar);
1228 setCursor(bview, selection.start.par(), selection.start.pos());
1229 selection.cursor = cursor;
1230 setCursor(bview, selection.end.par(), selection.end.pos());
1231 setSelection(bview);
1232 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1234 bview->updateInset(inset_owner, true);
1238 char loweralphaCounter(int n)
1240 if (n < 1 || n > 26)
1250 char alphaCounter(int n)
1252 if (n < 1 || n > 26)
1260 char hebrewCounter(int n)
1262 static const char hebrew[22] = {
1263 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1264 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1265 '÷', 'ø', 'ù', 'ú'
1267 if (n < 1 || n > 22)
1275 string const romanCounter(int n)
1277 static char const * roman[20] = {
1278 "i", "ii", "iii", "iv", "v",
1279 "vi", "vii", "viii", "ix", "x",
1280 "xi", "xii", "xiii", "xiv", "xv",
1281 "xvi", "xvii", "xviii", "xix", "xx"
1283 if (n < 1 || n > 20)
1292 // set the counter of a paragraph. This includes the labels
1293 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1295 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1296 LyXLayout const & layout = textclass[par->layout()];
1299 // copy the prev-counters to this one,
1300 // unless this is the first paragraph
1301 if (par->previous()) {
1302 for (int i = 0; i < 10; ++i) {
1303 par->setCounter(i, par->previous()->getFirstCounter(i));
1305 par->params().appendix(par->previous()->params().appendix());
1306 if (!par->params().appendix() && par->params().startOfAppendix()) {
1307 par->params().appendix(true);
1308 for (int i = 0; i < 10; ++i) {
1309 par->setCounter(i, 0);
1312 par->enumdepth = par->previous()->enumdepth;
1313 par->itemdepth = par->previous()->itemdepth;
1315 for (int i = 0; i < 10; ++i) {
1316 par->setCounter(i, 0);
1318 par->params().appendix(par->params().startOfAppendix());
1323 /* Maybe we have to increment the enumeration depth.
1324 * BUT, enumeration in a footnote is considered in isolation from its
1325 * surrounding paragraph so don't increment if this is the
1326 * first line of the footnote
1327 * AND, bibliographies can't have their depth changed ie. they
1328 * are always of depth 0
1331 && par->previous()->getDepth() < par->getDepth()
1332 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1333 && par->enumdepth < 3
1334 && layout.labeltype != LABEL_BIBLIO) {
1338 // Maybe we have to decrement the enumeration depth, see note above
1340 && par->previous()->getDepth() > par->getDepth()
1341 && layout.labeltype != LABEL_BIBLIO) {
1342 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1343 par->setCounter(6 + par->enumdepth,
1344 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1345 /* reset the counters.
1346 * A depth change is like a breaking layout
1348 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1349 par->setCounter(i, 0);
1352 if (!par->params().labelString().empty()) {
1353 par->params().labelString(string());
1356 if (layout.margintype == MARGIN_MANUAL) {
1357 if (par->params().labelWidthString().empty()) {
1358 par->setLabelWidthString(layout.labelstring());
1361 par->setLabelWidthString(string());
1364 // is it a layout that has an automatic label?
1365 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1367 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1368 if (i >= 0 && i<= buf->params.secnumdepth) {
1369 par->incCounter(i); // increment the counter
1371 // Is there a label? Useful for Chapter layout
1372 if (!par->params().appendix()) {
1373 if (!layout.labelstring().empty())
1374 par->params().labelString(layout.labelstring());
1376 par->params().labelString(string());
1378 if (!layout.labelstring_appendix().empty())
1379 par->params().labelString(layout.labelstring_appendix());
1381 par->params().labelString(string());
1386 if (!par->params().appendix()) {
1387 switch (2 * LABEL_COUNTER_CHAPTER -
1388 textclass.maxcounter() + i) {
1389 case LABEL_COUNTER_CHAPTER:
1390 s << par->getCounter(i);
1392 case LABEL_COUNTER_SECTION:
1393 s << par->getCounter(i - 1) << '.'
1394 << par->getCounter(i);
1396 case LABEL_COUNTER_SUBSECTION:
1397 s << par->getCounter(i - 2) << '.'
1398 << par->getCounter(i - 1) << '.'
1399 << par->getCounter(i);
1401 case LABEL_COUNTER_SUBSUBSECTION:
1402 s << par->getCounter(i - 3) << '.'
1403 << par->getCounter(i - 2) << '.'
1404 << par->getCounter(i - 1) << '.'
1405 << par->getCounter(i);
1408 case LABEL_COUNTER_PARAGRAPH:
1409 s << par->getCounter(i - 4) << '.'
1410 << par->getCounter(i - 3) << '.'
1411 << par->getCounter(i - 2) << '.'
1412 << par->getCounter(i - 1) << '.'
1413 << par->getCounter(i);
1415 case LABEL_COUNTER_SUBPARAGRAPH:
1416 s << par->getCounter(i - 5) << '.'
1417 << par->getCounter(i - 4) << '.'
1418 << par->getCounter(i - 3) << '.'
1419 << par->getCounter(i - 2) << '.'
1420 << par->getCounter(i - 1) << '.'
1421 << par->getCounter(i);
1425 // Can this ever be reached? And in the
1426 // case it is, how can this be correct?
1428 s << par->getCounter(i) << '.';
1431 } else { // appendix
1432 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1433 case LABEL_COUNTER_CHAPTER:
1434 if (par->isRightToLeftPar(buf->params))
1435 s << hebrewCounter(par->getCounter(i));
1437 s << alphaCounter(par->getCounter(i));
1439 case LABEL_COUNTER_SECTION:
1440 if (par->isRightToLeftPar(buf->params))
1441 s << hebrewCounter(par->getCounter(i - 1));
1443 s << alphaCounter(par->getCounter(i - 1));
1446 << par->getCounter(i);
1449 case LABEL_COUNTER_SUBSECTION:
1450 if (par->isRightToLeftPar(buf->params))
1451 s << hebrewCounter(par->getCounter(i - 2));
1453 s << alphaCounter(par->getCounter(i - 2));
1456 << par->getCounter(i-1) << '.'
1457 << par->getCounter(i);
1460 case LABEL_COUNTER_SUBSUBSECTION:
1461 if (par->isRightToLeftPar(buf->params))
1462 s << hebrewCounter(par->getCounter(i-3));
1464 s << alphaCounter(par->getCounter(i-3));
1467 << par->getCounter(i-2) << '.'
1468 << par->getCounter(i-1) << '.'
1469 << par->getCounter(i);
1472 case LABEL_COUNTER_PARAGRAPH:
1473 if (par->isRightToLeftPar(buf->params))
1474 s << hebrewCounter(par->getCounter(i-4));
1476 s << alphaCounter(par->getCounter(i-4));
1479 << par->getCounter(i-3) << '.'
1480 << par->getCounter(i-2) << '.'
1481 << par->getCounter(i-1) << '.'
1482 << par->getCounter(i);
1485 case LABEL_COUNTER_SUBPARAGRAPH:
1486 if (par->isRightToLeftPar(buf->params))
1487 s << hebrewCounter(par->getCounter(i-5));
1489 s << alphaCounter(par->getCounter(i-5));
1492 << par->getCounter(i-4) << '.'
1493 << par->getCounter(i-3) << '.'
1494 << par->getCounter(i-2) << '.'
1495 << par->getCounter(i-1) << '.'
1496 << par->getCounter(i);
1500 // Can this ever be reached? And in the
1501 // case it is, how can this be correct?
1503 s << par->getCounter(i) << '.';
1509 par->params().labelString(par->params().labelString() +s.str().c_str());
1510 // We really want to remove the c_str as soon as
1513 for (i++; i < 10; ++i) {
1514 // reset the following counters
1515 par->setCounter(i, 0);
1517 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1518 for (i++; i < 10; ++i) {
1519 // reset the following counters
1520 par->setCounter(i, 0);
1522 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1523 par->incCounter(i + par->enumdepth);
1524 int number = par->getCounter(i + par->enumdepth);
1528 switch (par->enumdepth) {
1530 if (par->isRightToLeftPar(buf->params))
1532 << hebrewCounter(number)
1536 << loweralphaCounter(number)
1540 if (par->isRightToLeftPar(buf->params))
1541 s << '.' << romanCounter(number);
1543 s << romanCounter(number) << '.';
1546 if (par->isRightToLeftPar(buf->params))
1548 << alphaCounter(number);
1550 s << alphaCounter(number)
1554 if (par->isRightToLeftPar(buf->params))
1561 par->params().labelString(s.str().c_str());
1563 for (i += par->enumdepth + 1; i < 10; ++i) {
1564 // reset the following counters
1565 par->setCounter(i, 0);
1569 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1570 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1572 int number = par->getCounter(i);
1574 InsetCommandParams p("bibitem" );
1575 par->bibkey = new InsetBibKey(p);
1577 par->bibkey->setCounter(number);
1578 par->params().labelString(layout.labelstring());
1580 // In biblio should't be following counters but...
1582 string s = layout.labelstring();
1584 // the caption hack:
1585 if (layout.labeltype == LABEL_SENSITIVE) {
1586 bool isOK (par->inInset() && par->inInset()->owner() &&
1587 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1590 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1592 = floatList.getType(tmp->type());
1593 // We should get the correct number here too.
1594 s = fl.name() + " #:";
1596 /* par->SetLayout(0);
1597 s = layout->labelstring; */
1598 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1599 ? " :úåòîùî øñç" : "Senseless: ";
1602 par->params().labelString(s);
1604 /* reset the enumeration counter. They are always resetted
1605 * when there is any other layout between */
1606 for (int i = 6 + par->enumdepth; i < 10; ++i)
1607 par->setCounter(i, 0);
1612 // Updates all counters BEHIND the row. Changed paragraphs
1613 // with a dynamic left margin will be rebroken.
1614 void LyXText::updateCounters(BufferView * bview, Row * row) const
1622 par = row->par()->next();
1626 while (row->par() != par)
1629 setCounter(bview->buffer(), par);
1631 // now check for the headline layouts. remember that they
1632 // have a dynamic left margin
1633 LyXTextClass const & tclass =
1634 textclasslist[bview->buffer()->params.textclass];
1635 LyXLayout const & layout = tclass[par->layout()];
1637 if (layout.margintype == MARGIN_DYNAMIC
1638 || layout.labeltype == LABEL_SENSITIVE) {
1639 // Rebreak the paragraph
1640 removeParagraph(row);
1641 appendParagraph(bview, row);
1648 void LyXText::insertInset(BufferView * bview, Inset * inset)
1650 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1652 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1654 cursor.par()->insertInset(cursor.pos(), inset);
1655 // Just to rebreak and refresh correctly.
1656 // The character will not be inserted a second time
1657 insertChar(bview, Paragraph::META_INSET);
1658 // If we enter a highly editable inset the cursor should be to before
1659 // the inset. This couldn't happen before as Undo was not handled inside
1660 // inset now after the Undo LyX tries to call inset->Edit(...) again
1661 // and cannot do this as the cursor is behind the inset and GetInset
1662 // does not return the inset!
1663 if (isHighlyEditableInset(inset)) {
1664 cursorLeft(bview, true);
1670 void LyXText::copyEnvironmentType()
1672 copylayouttype = cursor.par()->layout();
1676 void LyXText::pasteEnvironmentType(BufferView * bview)
1678 setLayout(bview, copylayouttype);
1682 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1684 // Stuff what we got on the clipboard. Even if there is no selection.
1686 // There is a problem with having the stuffing here in that the
1687 // larger the selection the slower LyX will get. This can be
1688 // solved by running the line below only when the selection has
1689 // finished. The solution used currently just works, to make it
1690 // faster we need to be more clever and probably also have more
1691 // calls to stuffClipboard. (Lgb)
1692 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1694 // This doesn't make sense, if there is no selection
1695 if (!selection.set())
1698 // OK, we have a selection. This is always between selection.start
1699 // and selection.end
1701 // make sure that the depth behind the selection are restored, too
1702 Paragraph * endpar = selection.end.par()->next();
1703 Paragraph * undoendpar = endpar;
1705 if (endpar && endpar->getDepth()) {
1706 while (endpar && endpar->getDepth()) {
1707 endpar = endpar->next();
1708 undoendpar = endpar;
1710 } else if (endpar) {
1711 endpar = endpar->next(); // because of parindents etc.
1714 setUndo(bview, Undo::DELETE,
1715 selection.start.par(), undoendpar);
1717 // there are two cases: cut only within one paragraph or
1718 // more than one paragraph
1719 if (selection.start.par() == selection.end.par()) {
1720 // only within one paragraph
1721 endpar = selection.end.par();
1722 int pos = selection.end.pos();
1723 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1724 selection.start.pos(), pos,
1725 bview->buffer()->params.textclass,
1727 selection.end.pos(pos);
1729 endpar = selection.end.par();
1730 int pos = selection.end.pos();
1731 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1732 selection.start.pos(), pos,
1733 bview->buffer()->params.textclass,
1736 selection.end.par(endpar);
1737 selection.end.pos(pos);
1738 cursor.pos(selection.end.pos());
1740 endpar = endpar->next();
1742 // sometimes necessary
1744 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1746 redoParagraphs(bview, selection.start, endpar);
1748 // cutSelection can invalidate the cursor so we need to set
1750 cursor = selection.start;
1752 // need a valid cursor. (Lgb)
1755 setCursor(bview, cursor.par(), cursor.pos());
1756 selection.cursor = cursor;
1757 updateCounters(bview, cursor.row());
1761 void LyXText::copySelection(BufferView * bview)
1763 // stuff the selection onto the X clipboard, from an explicit copy request
1764 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1766 // this doesnt make sense, if there is no selection
1767 if (!selection.set())
1770 // ok we have a selection. This is always between selection.start
1771 // and sel_end cursor
1773 // copy behind a space if there is one
1774 while (selection.start.par()->size() > selection.start.pos()
1775 && selection.start.par()->isLineSeparator(selection.start.pos())
1776 && (selection.start.par() != selection.end.par()
1777 || selection.start.pos() < selection.end.pos()))
1778 selection.start.pos(selection.start.pos() + 1);
1780 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1781 selection.start.pos(), selection.end.pos(),
1782 bview->buffer()->params.textclass);
1786 void LyXText::pasteSelection(BufferView * bview)
1788 // this does not make sense, if there is nothing to paste
1789 if (!CutAndPaste::checkPastePossible(cursor.par()))
1792 setUndo(bview, Undo::INSERT,
1793 cursor.par(), cursor.par()->next());
1796 Paragraph * actpar = cursor.par();
1797 int pos = cursor.pos();
1799 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1800 bview->buffer()->params.textclass);
1802 redoParagraphs(bview, cursor, endpar);
1804 setCursor(bview, cursor.par(), cursor.pos());
1807 selection.cursor = cursor;
1808 setCursor(bview, actpar, pos);
1809 setSelection(bview);
1810 updateCounters(bview, cursor.row());
1814 // sets the selection over the number of characters of string, no check!!
1815 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1820 selection.cursor = cursor;
1821 for (string::size_type i = 0; i < str.length(); ++i)
1823 setSelection(bview);
1827 // simple replacing. The font of the first selected character is used
1828 void LyXText::replaceSelectionWithString(BufferView * bview,
1831 setCursorParUndo(bview);
1834 if (!selection.set()) { // create a dummy selection
1835 selection.end = cursor;
1836 selection.start = cursor;
1839 // Get font setting before we cut
1840 pos_type pos = selection.end.pos();
1841 LyXFont const font = selection.start.par()
1842 ->getFontSettings(bview->buffer()->params,
1843 selection.start.pos());
1845 // Insert the new string
1846 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1847 selection.end.par()->insertChar(pos, (*cit), font);
1851 // Cut the selection
1852 cutSelection(bview, true, false);
1858 // needed to insert the selection
1859 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1861 Paragraph * par = cursor.par();
1862 pos_type pos = cursor.pos();
1863 Paragraph * endpar = cursor.par()->next();
1865 setCursorParUndo(bview);
1867 // only to be sure, should not be neccessary
1870 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1872 redoParagraphs(bview, cursor, endpar);
1873 setCursor(bview, cursor.par(), cursor.pos());
1874 selection.cursor = cursor;
1875 setCursor(bview, par, pos);
1876 setSelection(bview);
1880 // turns double-CR to single CR, others where converted into one
1881 // blank. Then InsertStringAsLines is called
1882 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1884 string linestr(str);
1885 bool newline_inserted = false;
1886 for (string::size_type i = 0; i < linestr.length(); ++i) {
1887 if (linestr[i] == '\n') {
1888 if (newline_inserted) {
1889 // we know that \r will be ignored by
1890 // InsertStringA. Of course, it is a dirty
1891 // trick, but it works...
1892 linestr[i - 1] = '\r';
1896 newline_inserted = true;
1898 } else if (IsPrintable(linestr[i])) {
1899 newline_inserted = false;
1902 insertStringAsLines(bview, linestr);
1906 bool LyXText::gotoNextInset(BufferView * bview,
1907 vector<Inset::Code> const & codes,
1908 string const & contents) const
1910 LyXCursor res = cursor;
1913 if (res.pos() < res.par()->size() - 1) {
1914 res.pos(res.pos() + 1);
1916 res.par(res.par()->next());
1920 } while (res.par() &&
1921 !(res.par()->isInset(res.pos())
1922 && (inset = res.par()->getInset(res.pos())) != 0
1923 && find(codes.begin(), codes.end(), inset->lyxCode())
1925 && (contents.empty() ||
1926 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1930 setCursor(bview, res.par(), res.pos(), false);
1937 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1940 LyXCursor tmpcursor;
1944 Row * row = getRow(par, pos, y);
1946 // is there a break one row above
1947 if (row->previous() && row->previous()->par() == row->par()) {
1948 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1949 if (z >= row->pos()) {
1950 // set the dimensions of the row above
1951 y -= row->previous()->height();
1953 refresh_row = row->previous();
1954 status(bview, LyXText::NEED_MORE_REFRESH);
1956 breakAgain(bview, row->previous());
1958 // set the cursor again. Otherwise
1959 // dangling pointers are possible
1960 setCursor(bview, cursor.par(), cursor.pos(),
1961 false, cursor.boundary());
1962 selection.cursor = cursor;
1967 int const tmpheight = row->height();
1968 pos_type const tmplast = rowLast(row);
1972 breakAgain(bview, row);
1973 if (row->height() == tmpheight && rowLast(row) == tmplast)
1974 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1976 status(bview, LyXText::NEED_MORE_REFRESH);
1978 // check the special right address boxes
1979 if (textclasslist[bview->buffer()->params.textclass][
1980 par->layout()].margintype
1981 == MARGIN_RIGHT_ADDRESS_BOX)
1989 redoDrawingOfParagraph(bview, tmpcursor);
1992 // set the cursor again. Otherwise dangling pointers are possible
1993 // also set the selection
1995 if (selection.set()) {
1997 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1998 false, selection.cursor.boundary());
1999 selection.cursor = cursor;
2000 setCursorIntern(bview, selection.start.par(),
2001 selection.start.pos(),
2002 false, selection.start.boundary());
2003 selection.start = cursor;
2004 setCursorIntern(bview, selection.end.par(),
2005 selection.end.pos(),
2006 false, selection.end.boundary());
2007 selection.end = cursor;
2008 setCursorIntern(bview, last_sel_cursor.par(),
2009 last_sel_cursor.pos(),
2010 false, last_sel_cursor.boundary());
2011 last_sel_cursor = cursor;
2014 setCursorIntern(bview, cursor.par(), cursor.pos(),
2015 false, cursor.boundary());
2019 // returns false if inset wasn't found
2020 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2022 // first check the current paragraph
2023 int pos = cursor.par()->getPositionOfInset(inset);
2025 checkParagraph(bview, cursor.par(), pos);
2029 // check every paragraph
2031 Paragraph * par = ownerParagraph();
2033 pos = par->getPositionOfInset(inset);
2035 checkParagraph(bview, par, pos);
2045 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2047 bool setfont, bool boundary) const
2049 LyXCursor old_cursor = cursor;
2050 setCursorIntern(bview, par, pos, setfont, boundary);
2051 return deleteEmptyParagraphMechanism(bview, old_cursor);
2055 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2056 pos_type pos, bool boundary) const
2063 cur.boundary(boundary);
2065 // get the cursor y position in text
2067 Row * row = getRow(par, pos, y);
2068 Row * old_row = row;
2070 // if we are before the first char of this row and are still in the
2071 // same paragraph and there is a previous row then put the cursor on
2072 // the end of the previous row
2073 cur.iy(y + row->baseline());
2075 if (row->previous() && pos &&
2076 par->getChar(pos) == Paragraph::META_INSET &&
2077 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2079 row = row->previous();
2084 // y is now the beginning of the cursor row
2085 y += row->baseline();
2086 // y is now the cursor baseline
2089 pos_type last = rowLastPrintable(old_row);
2091 if (pos > last + 1) {
2092 // This shouldn't happen.
2095 } else if (pos < row->pos()) {
2100 // now get the cursors x position
2101 float x = getCursorX(bview, row, pos, last, boundary);
2104 if (old_row != row) {
2105 x = getCursorX(bview, old_row, pos, last, boundary);
2112 float LyXText::getCursorX(BufferView * bview, Row * row,
2113 pos_type pos, pos_type last, bool boundary) const
2115 pos_type cursor_vpos = 0;
2117 float fill_separator;
2119 float fill_label_hfill;
2120 // This call HAS to be here because of the BidiTables!!!
2121 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2124 if (last < row->pos())
2125 cursor_vpos = row->pos();
2126 else if (pos > last && !boundary)
2127 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2128 ? row->pos() : last + 1;
2129 else if (pos > row->pos() &&
2130 (pos > last || boundary))
2131 /// Place cursor after char at (logical) position pos - 1
2132 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2133 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2135 /// Place cursor before char at (logical) position pos
2136 cursor_vpos = (bidi_level(pos) % 2 == 0)
2137 ? log2vis(pos) : log2vis(pos) + 1;
2139 pos_type main_body =
2140 beginningOfMainBody(bview->buffer(), row->par());
2141 if ((main_body > 0) &&
2142 ((main_body-1 > last) ||
2143 !row->par()->isLineSeparator(main_body-1)))
2146 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2147 pos_type pos = vis2log(vpos);
2148 if (main_body > 0 && pos == main_body - 1) {
2149 x += fill_label_hfill +
2150 lyxfont::width(textclasslist[
2151 bview->buffer()->params.textclass][
2152 row->par()->layout()]
2154 getLabelFont(bview->buffer(), row->par()));
2155 if (row->par()->isLineSeparator(main_body-1))
2156 x -= singleWidth(bview, row->par(),main_body-1);
2158 if (hfillExpansion(bview->buffer(), row, pos)) {
2159 x += singleWidth(bview, row->par(), pos);
2160 if (pos >= main_body)
2163 x += fill_label_hfill;
2164 } else if (row->par()->isSeparator(pos)) {
2165 x += singleWidth(bview, row->par(), pos);
2166 if (pos >= main_body)
2167 x += fill_separator;
2169 x += singleWidth(bview, row->par(), pos);
2175 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2176 pos_type pos, bool setfont, bool boundary) const
2178 InsetText * it = static_cast<InsetText *>(par->inInset());
2180 if (it != inset_owner) {
2181 lyxerr[Debug::INSETS] << "InsetText is " << it
2183 << "inset_owner is "
2184 << inset_owner << endl;
2185 #ifdef WITH_WARNINGS
2186 #warning I believe this code is wrong. (Lgb)
2187 #warning Jürgen, have a look at this. (Lgb)
2188 #warning Hmmm, I guess you are right but we
2189 #warning should verify when this is needed
2191 // Jürgen, would you like to have a look?
2192 // I guess we need to move the outer cursor
2193 // and open and lock the inset (bla bla bla)
2194 // stuff I don't know... so can you have a look?
2196 // I moved the lyxerr stuff in here so we can see if
2197 // this is actually really needed and where!
2199 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2204 setCursor(bview, cursor, par, pos, boundary);
2206 setCurrentFont(bview);
2210 void LyXText::setCurrentFont(BufferView * bview) const
2212 pos_type pos = cursor.pos();
2213 if (cursor.boundary() && pos > 0)
2217 if (pos == cursor.par()->size())
2219 else // potentional bug... BUG (Lgb)
2220 if (cursor.par()->isSeparator(pos)) {
2221 if (pos > cursor.row()->pos() &&
2222 bidi_level(pos) % 2 ==
2223 bidi_level(pos - 1) % 2)
2225 else if (pos + 1 < cursor.par()->size())
2231 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2232 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2234 if (cursor.pos() == cursor.par()->size() &&
2235 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2236 !cursor.boundary()) {
2237 Language const * lang =
2238 cursor.par()->getParLanguage(bview->buffer()->params);
2239 current_font.setLanguage(lang);
2240 current_font.setNumber(LyXFont::OFF);
2241 real_current_font.setLanguage(lang);
2242 real_current_font.setNumber(LyXFont::OFF);
2247 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2249 LyXCursor old_cursor = cursor;
2251 setCursorFromCoordinates(bview, cursor, x, y);
2252 setCurrentFont(bview);
2253 deleteEmptyParagraphMechanism(bview, old_cursor);
2257 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2260 // Get the row first.
2262 Row * row = getRowNearY(y);
2264 pos_type const column = getColumnNearX(bview, row, x, bound);
2265 cur.par(row->par());
2266 cur.pos(row->pos() + column);
2268 cur.y(y + row->baseline());
2271 if (row->next() && row->next()->pos() == cur.pos() &&
2272 cur.par() == row->next()->par() &&
2273 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2274 (ins=cur.par()->getInset(cur.pos())) &&
2275 (ins->needFullRow() || ins->display()))
2277 // we enter here if we put the cursor on the end of the row before
2278 // a inset which uses a full row and in that case we HAVE to calculate
2279 // the right (i) values.
2280 pos_type last = rowLastPrintable(row);
2281 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2283 cur.iy(y + row->height() + row->next()->baseline());
2284 cur.irow(row->next());
2290 cur.boundary(bound);
2294 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2296 if (cursor.pos() > 0) {
2297 bool boundary = cursor.boundary();
2298 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2299 if (!internal && !boundary &&
2300 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2301 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2302 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2303 Paragraph * par = cursor.par()->previous();
2304 setCursor(bview, par, par->size());
2309 void LyXText::cursorRight(BufferView * bview, bool internal) const
2311 if (!internal && cursor.boundary() &&
2312 !cursor.par()->isNewline(cursor.pos()))
2313 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2314 else if (cursor.pos() < cursor.par()->size()) {
2315 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2317 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2318 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2319 } else if (cursor.par()->next())
2320 setCursor(bview, cursor.par()->next(), 0);
2324 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2327 int x = cursor.x_fix();
2328 int y = cursor.y() - cursor.row()->baseline() - 1;
2329 setCursorFromCoordinates(bview, x, y);
2331 int y1 = cursor.iy() - first_y;
2335 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2336 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2337 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2341 setCursorFromCoordinates(bview, cursor.x_fix(),
2342 cursor.y() - cursor.row()->baseline() - 1);
2347 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2350 int x = cursor.x_fix();
2351 int y = cursor.y() - cursor.row()->baseline() +
2352 cursor.row()->height() + 1;
2353 setCursorFromCoordinates(bview, x, y);
2354 if (!selecting && cursor.row() == cursor.irow()) {
2355 int y1 = cursor.iy() - first_y;
2359 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2360 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2361 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2365 setCursorFromCoordinates(bview, cursor.x_fix(),
2366 cursor.y() - cursor.row()->baseline()
2367 + cursor.row()->height() + 1);
2372 void LyXText::cursorUpParagraph(BufferView * bview) const
2374 if (cursor.pos() > 0) {
2375 setCursor(bview, cursor.par(), 0);
2377 else if (cursor.par()->previous()) {
2378 setCursor(bview, cursor.par()->previous(), 0);
2383 void LyXText::cursorDownParagraph(BufferView * bview) const
2385 if (cursor.par()->next()) {
2386 setCursor(bview, cursor.par()->next(), 0);
2388 setCursor(bview, cursor.par(), cursor.par()->size());
2392 // fix the cursor `cur' after a characters has been deleted at `where'
2393 // position. Called by deleteEmptyParagraphMechanism
2394 void LyXText::fixCursorAfterDelete(BufferView * bview,
2396 LyXCursor const & where) const
2398 // if cursor is not in the paragraph where the delete occured,
2400 if (cur.par() != where.par())
2403 // if cursor position is after the place where the delete occured,
2405 if (cur.pos() > where.pos())
2406 cur.pos(cur.pos()-1);
2408 // recompute row et al. for this cursor
2409 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2413 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2414 LyXCursor const & old_cursor) const
2416 // Would be wrong to delete anything if we have a selection.
2417 if (selection.set())
2420 // We allow all kinds of "mumbo-jumbo" when freespacing.
2421 if (textclasslist[bview->buffer()->params.textclass][
2422 old_cursor.par()->layout()].free_spacing
2423 || old_cursor.par()->isFreeSpacing())
2428 /* Ok I'll put some comments here about what is missing.
2429 I have fixed BackSpace (and thus Delete) to not delete
2430 double-spaces automagically. I have also changed Cut,
2431 Copy and Paste to hopefully do some sensible things.
2432 There are still some small problems that can lead to
2433 double spaces stored in the document file or space at
2434 the beginning of paragraphs. This happens if you have
2435 the cursor betwenn to spaces and then save. Or if you
2436 cut and paste and the selection have a space at the
2437 beginning and then save right after the paste. I am
2438 sure none of these are very hard to fix, but I will
2439 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2440 that I can get some feedback. (Lgb)
2443 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2444 // delete the LineSeparator.
2447 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2448 // delete the LineSeparator.
2451 // If the pos around the old_cursor were spaces, delete one of them.
2452 if (old_cursor.par() != cursor.par()
2453 || old_cursor.pos() != cursor.pos()) {
2454 // Only if the cursor has really moved
2456 if (old_cursor.pos() > 0
2457 && old_cursor.pos() < old_cursor.par()->size()
2458 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2459 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2460 old_cursor.par()->erase(old_cursor.pos() - 1);
2461 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2463 #ifdef WITH_WARNINGS
2464 #warning This will not work anymore when we have multiple views of the same buffer
2465 // In this case, we will have to correct also the cursors held by
2466 // other bufferviews. It will probably be easier to do that in a more
2467 // automated way in LyXCursor code. (JMarc 26/09/2001)
2469 // correct all cursors held by the LyXText
2470 fixCursorAfterDelete(bview, cursor, old_cursor);
2471 fixCursorAfterDelete(bview, selection.cursor,
2473 fixCursorAfterDelete(bview, selection.start,
2475 fixCursorAfterDelete(bview, selection.end, old_cursor);
2476 fixCursorAfterDelete(bview, last_sel_cursor,
2478 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2479 fixCursorAfterDelete(bview, toggle_end_cursor,
2485 // don't delete anything if this is the ONLY paragraph!
2486 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2489 // Do not delete empty paragraphs with keepempty set.
2491 [bview->buffer()->params.textclass]
2492 [old_cursor.par()->layout()].keepempty)
2495 // only do our magic if we changed paragraph
2496 if (old_cursor.par() == cursor.par())
2499 // record if we have deleted a paragraph
2500 // we can't possibly have deleted a paragraph before this point
2501 bool deleted = false;
2503 if ((old_cursor.par()->size() == 0
2504 || (old_cursor.par()->size() == 1
2505 && old_cursor.par()->isLineSeparator(0)))) {
2506 // ok, we will delete anything
2507 LyXCursor tmpcursor;
2509 // make sure that you do not delete any environments
2510 status(bview, LyXText::NEED_MORE_REFRESH);
2513 if (old_cursor.row()->previous()) {
2514 refresh_row = old_cursor.row()->previous();
2515 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2517 cursor = old_cursor; // that undo can restore the right cursor position
2518 Paragraph * endpar = old_cursor.par()->next();
2519 if (endpar && endpar->getDepth()) {
2520 while (endpar && endpar->getDepth()) {
2521 endpar = endpar->next();
2524 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2528 removeRow(old_cursor.row());
2529 if (ownerParagraph() == old_cursor.par()) {
2530 ownerParagraph(ownerParagraph()->next());
2533 delete old_cursor.par();
2535 /* Breakagain the next par. Needed because of
2536 * the parindent that can occur or dissappear.
2537 * The next row can change its height, if
2538 * there is another layout before */
2539 if (refresh_row->next()) {
2540 breakAgain(bview, refresh_row->next());
2541 updateCounters(bview, refresh_row);
2543 setHeightOfRow(bview, refresh_row);
2545 refresh_row = old_cursor.row()->next();
2546 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2549 cursor = old_cursor; // that undo can restore the right cursor position
2550 Paragraph * endpar = old_cursor.par()->next();
2551 if (endpar && endpar->getDepth()) {
2552 while (endpar && endpar->getDepth()) {
2553 endpar = endpar->next();
2556 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2560 removeRow(old_cursor.row());
2562 if (ownerParagraph() == old_cursor.par()) {
2563 ownerParagraph(ownerParagraph()->next());
2566 delete old_cursor.par();
2568 /* Breakagain the next par. Needed because of
2569 the parindent that can occur or dissappear.
2570 The next row can change its height, if
2571 there is another layout before */
2573 breakAgain(bview, refresh_row);
2574 updateCounters(bview, refresh_row->previous());
2579 setCursorIntern(bview, cursor.par(), cursor.pos());
2581 if (selection.cursor.par() == old_cursor.par()
2582 && selection.cursor.pos() == old_cursor.pos()) {
2583 // correct selection
2584 selection.cursor = cursor;
2588 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2589 redoParagraphs(bview, old_cursor,
2590 old_cursor.par()->next());
2592 setCursorIntern(bview, cursor.par(), cursor.pos());
2593 selection.cursor = cursor;
2600 void LyXText::toggleAppendix(BufferView * bview)
2602 Paragraph * par = cursor.par();
2603 bool start = !par->params().startOfAppendix();
2605 // ensure that we have only one start_of_appendix in this document
2606 Paragraph * tmp = ownerParagraph();
2607 for (; tmp; tmp = tmp->next()) {
2608 tmp->params().startOfAppendix(false);
2611 par->params().startOfAppendix(start);
2613 // we can set the refreshing parameters now
2614 status(bview, LyXText::NEED_MORE_REFRESH);
2616 refresh_row = 0; // not needed for full update
2617 updateCounters(bview, 0);
2618 setCursor(bview, cursor.par(), cursor.pos());
2622 Paragraph * LyXText::ownerParagraph() const
2625 return inset_owner->paragraph();
2627 return bv_owner->buffer()->paragraph;
2631 void LyXText::ownerParagraph(Paragraph * p) const
2634 inset_owner->paragraph(p);
2636 bv_owner->buffer()->paragraph = p;
2641 void LyXText::ownerParagraph(int id, Paragraph * p) const
2643 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2644 if (op && op->inInset()) {
2645 static_cast<InsetText *>(op->inInset())->paragraph(p);
2652 LyXText::text_status LyXText::status() const
2658 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2660 // well as much as I know && binds more then || so the above and the
2661 // below are identical (this for your known use of parentesis!)
2662 // Now some explanation:
2663 // We should only go up with refreshing code so this means that if
2664 // we have a MORE refresh we should never set it to LITTLE if we still
2665 // didn't handle it (and then it will be UNCHANGED. Now as long as
2666 // we stay inside one LyXText this may work but we need to tell the
2667 // outermost LyXText that it should REALLY draw us if there is some
2668 // change in a Inset::LyXText. So you see that when we are inside a
2669 // inset's LyXText we give the LITTLE to the outermost LyXText to
2670 // tell'em that it should redraw the actual row (where the inset
2671 // resides! Capito?!
2673 if ((status_ != NEED_MORE_REFRESH)
2674 || (status_ == NEED_MORE_REFRESH
2675 && st != NEED_VERY_LITTLE_REFRESH))
2678 if (inset_owner && st != UNCHANGED) {
2679 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2680 if (!bview->text->refresh_row) {
2681 bview->text->refresh_row = bview->text->cursor.row();
2682 bview->text->refresh_y = bview->text->cursor.y() -
2683 bview->text->cursor.row()->baseline();