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 void LyXText::fullRebreak(BufferView * bview)
909 if (need_break_row) {
910 breakAgain(bview, need_break_row);
917 // important for the screen
920 /* the cursor set functions have a special mechanism. When they
921 * realize, that you left an empty paragraph, they will delete it.
922 * They also delete the corresponding row */
924 // need the selection cursor:
925 void LyXText::setSelection(BufferView * bview)
927 bool const lsel = selection.set();
929 if (!selection.set()) {
930 last_sel_cursor = selection.cursor;
931 selection.start = selection.cursor;
932 selection.end = selection.cursor;
937 // first the toggling area
938 if (cursor.y() < last_sel_cursor.y()
939 || (cursor.y() == last_sel_cursor.y()
940 && cursor.x() < last_sel_cursor.x())) {
941 toggle_end_cursor = last_sel_cursor;
942 toggle_cursor = cursor;
944 toggle_end_cursor = cursor;
945 toggle_cursor = last_sel_cursor;
948 last_sel_cursor = cursor;
950 // and now the whole selection
952 if (selection.cursor.par() == cursor.par())
953 if (selection.cursor.pos() < cursor.pos()) {
954 selection.end = cursor;
955 selection.start = selection.cursor;
957 selection.end = selection.cursor;
958 selection.start = cursor;
960 else if (selection.cursor.y() < cursor.y() ||
961 (selection.cursor.y() == cursor.y()
962 && selection.cursor.x() < cursor.x())) {
963 selection.end = cursor;
964 selection.start = selection.cursor;
967 selection.end = selection.cursor;
968 selection.start = cursor;
971 // a selection with no contents is not a selection
972 if (selection.start.par() == selection.end.par() &&
973 selection.start.pos() == selection.end.pos())
974 selection.set(false);
976 if (inset_owner && (selection.set() || lsel))
977 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
981 string const LyXText::selectionAsString(Buffer const * buffer,
984 if (!selection.set()) return string();
987 // Special handling if the whole selection is within one paragraph
988 if (selection.start.par() == selection.end.par()) {
989 result += selection.start.par()->asString(buffer,
990 selection.start.pos(),
996 // The selection spans more than one paragraph
998 // First paragraph in selection
999 result += selection.start.par()->asString(buffer,
1000 selection.start.pos(),
1001 selection.start.par()->size(),
1005 // The paragraphs in between (if any)
1006 LyXCursor tmpcur(selection.start);
1007 tmpcur.par(tmpcur.par()->next());
1008 while (tmpcur.par() != selection.end.par()) {
1009 result += tmpcur.par()->asString(buffer, 0,
1010 tmpcur.par()->size(),
1012 tmpcur.par(tmpcur.par()->next());
1015 // Last paragraph in selection
1016 result += selection.end.par()->asString(buffer, 0,
1017 selection.end.pos(), label);
1023 void LyXText::clearSelection() const
1025 selection.set(false);
1026 selection.mark(false);
1027 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1028 // reset this in the bv_owner!
1029 if (bv_owner && bv_owner->text)
1030 bv_owner->text->xsel_cache.set(false);
1034 void LyXText::cursorHome(BufferView * bview) const
1036 setCursor(bview, cursor.par(), cursor.row()->pos());
1040 void LyXText::cursorEnd(BufferView * bview) const
1042 if (!cursor.row()->next()
1043 || cursor.row()->next()->par() != cursor.row()->par()) {
1044 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1046 if (cursor.par()->size() &&
1047 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1048 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1049 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1051 setCursor(bview,cursor.par(),
1052 rowLast(cursor.row()) + 1);
1058 void LyXText::cursorTop(BufferView * bview) const
1060 while (cursor.par()->previous())
1061 cursor.par(cursor.par()->previous());
1062 setCursor(bview, cursor.par(), 0);
1066 void LyXText::cursorBottom(BufferView * bview) const
1068 while (cursor.par()->next())
1069 cursor.par(cursor.par()->next());
1070 setCursor(bview, cursor.par(), cursor.par()->size());
1074 void LyXText::toggleFree(BufferView * bview,
1075 LyXFont const & font, bool toggleall)
1077 // If the mask is completely neutral, tell user
1078 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1079 // Could only happen with user style
1080 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1084 // Try implicit word selection
1085 // If there is a change in the language the implicit word selection
1087 LyXCursor resetCursor = cursor;
1088 bool implicitSelection = (font.language() == ignore_language
1089 && font.number() == LyXFont::IGNORE)
1090 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1093 setFont(bview, font, toggleall);
1095 // Implicit selections are cleared afterwards
1096 //and cursor is set to the original position.
1097 if (implicitSelection) {
1099 cursor = resetCursor;
1100 setCursor(bview, cursor.par(), cursor.pos());
1101 selection.cursor = cursor;
1104 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1108 string LyXText::getStringToIndex(BufferView * bview)
1112 // Try implicit word selection
1113 // If there is a change in the language the implicit word selection
1115 LyXCursor resetCursor = cursor;
1116 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1118 if (!selection.set()) {
1119 bview->owner()->message(_("Nothing to index!"));
1122 if (selection.start.par() != selection.end.par()) {
1123 bview->owner()->message(_("Cannot index more than one paragraph!"));
1127 idxstring = selectionAsString(bview->buffer(), false);
1129 // Implicit selections are cleared afterwards
1130 //and cursor is set to the original position.
1131 if (implicitSelection) {
1133 cursor = resetCursor;
1134 setCursor(bview, cursor.par(), cursor.pos());
1135 selection.cursor = cursor;
1141 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1142 Paragraph const * par) const
1144 if (textclasslist[buf->params.textclass][
1145 par->layout()].labeltype != LABEL_MANUAL)
1148 return par->beginningOfMainBody();
1152 /* the DTP switches for paragraphs. LyX will store them in the
1153 * first physicla paragraph. When a paragraph is broken, the top settings
1154 * rest, the bottom settings are given to the new one. So I can make shure,
1155 * they do not duplicate themself and you cannnot make dirty things with
1158 void LyXText::setParagraph(BufferView * bview,
1159 bool line_top, bool line_bottom,
1160 bool pagebreak_top, bool pagebreak_bottom,
1161 VSpace const & space_top,
1162 VSpace const & space_bottom,
1163 Spacing const & spacing,
1165 string labelwidthstring,
1168 LyXCursor tmpcursor = cursor;
1169 if (!selection.set()) {
1170 selection.start = cursor;
1171 selection.end = cursor;
1174 // make sure that the depth behind the selection are restored, too
1175 Paragraph * endpar = selection.end.par()->next();
1176 Paragraph * undoendpar = endpar;
1178 if (endpar && endpar->getDepth()) {
1179 while (endpar && endpar->getDepth()) {
1180 endpar = endpar->next();
1181 undoendpar = endpar;
1185 // because of parindents etc.
1186 endpar = endpar->next();
1189 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1192 Paragraph * tmppar = selection.end.par();
1193 LyXTextClass const & tclass =
1194 textclasslist[bview->buffer()->params.textclass];
1196 while (tmppar != selection.start.par()->previous()) {
1197 setCursor(bview, tmppar, 0);
1198 status(bview, LyXText::NEED_MORE_REFRESH);
1199 refresh_row = cursor.row();
1200 refresh_y = cursor.y() - cursor.row()->baseline();
1201 cursor.par()->params().lineTop(line_top);
1202 cursor.par()->params().lineBottom(line_bottom);
1203 cursor.par()->params().pagebreakTop(pagebreak_top);
1204 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1205 cursor.par()->params().spaceTop(space_top);
1206 cursor.par()->params().spaceBottom(space_bottom);
1207 cursor.par()->params().spacing(spacing);
1208 // does the layout allow the new alignment?
1209 LyXLayout const & layout = tclass[cursor.par()->layout()];
1211 if (align == LYX_ALIGN_LAYOUT)
1212 align = layout.align;
1213 if (align & layout.alignpossible) {
1214 if (align == layout.align)
1215 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1217 cursor.par()->params().align(align);
1219 cursor.par()->setLabelWidthString(labelwidthstring);
1220 cursor.par()->params().noindent(noindent);
1221 tmppar = cursor.par()->previous();
1224 redoParagraphs(bview, selection.start, endpar);
1227 setCursor(bview, selection.start.par(), selection.start.pos());
1228 selection.cursor = cursor;
1229 setCursor(bview, selection.end.par(), selection.end.pos());
1230 setSelection(bview);
1231 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1233 bview->updateInset(inset_owner, true);
1237 char loweralphaCounter(int n)
1239 if (n < 1 || n > 26)
1249 char alphaCounter(int n)
1251 if (n < 1 || n > 26)
1259 char hebrewCounter(int n)
1261 static const char hebrew[22] = {
1262 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1263 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1264 '÷', 'ø', 'ù', 'ú'
1266 if (n < 1 || n > 22)
1274 string const romanCounter(int n)
1276 static char const * roman[20] = {
1277 "i", "ii", "iii", "iv", "v",
1278 "vi", "vii", "viii", "ix", "x",
1279 "xi", "xii", "xiii", "xiv", "xv",
1280 "xvi", "xvii", "xviii", "xix", "xx"
1282 if (n < 1 || n > 20)
1291 // set the counter of a paragraph. This includes the labels
1292 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1294 LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1295 LyXLayout const & layout = textclass[par->layout()];
1298 // copy the prev-counters to this one,
1299 // unless this is the first paragraph
1300 if (par->previous()) {
1301 for (int i = 0; i < 10; ++i) {
1302 par->setCounter(i, par->previous()->getFirstCounter(i));
1304 par->params().appendix(par->previous()->params().appendix());
1305 if (!par->params().appendix() && par->params().startOfAppendix()) {
1306 par->params().appendix(true);
1307 for (int i = 0; i < 10; ++i) {
1308 par->setCounter(i, 0);
1311 par->enumdepth = par->previous()->enumdepth;
1312 par->itemdepth = par->previous()->itemdepth;
1314 for (int i = 0; i < 10; ++i) {
1315 par->setCounter(i, 0);
1317 par->params().appendix(par->params().startOfAppendix());
1322 /* Maybe we have to increment the enumeration depth.
1323 * BUT, enumeration in a footnote is considered in isolation from its
1324 * surrounding paragraph so don't increment if this is the
1325 * first line of the footnote
1326 * AND, bibliographies can't have their depth changed ie. they
1327 * are always of depth 0
1330 && par->previous()->getDepth() < par->getDepth()
1331 && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1332 && par->enumdepth < 3
1333 && layout.labeltype != LABEL_BIBLIO) {
1337 // Maybe we have to decrement the enumeration depth, see note above
1339 && par->previous()->getDepth() > par->getDepth()
1340 && layout.labeltype != LABEL_BIBLIO) {
1341 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1342 par->setCounter(6 + par->enumdepth,
1343 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1344 /* reset the counters.
1345 * A depth change is like a breaking layout
1347 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1348 par->setCounter(i, 0);
1351 if (!par->params().labelString().empty()) {
1352 par->params().labelString(string());
1355 if (layout.margintype == MARGIN_MANUAL) {
1356 if (par->params().labelWidthString().empty()) {
1357 par->setLabelWidthString(layout.labelstring());
1360 par->setLabelWidthString(string());
1363 // is it a layout that has an automatic label?
1364 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1366 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1367 if (i >= 0 && i<= buf->params.secnumdepth) {
1368 par->incCounter(i); // increment the counter
1370 // Is there a label? Useful for Chapter layout
1371 if (!par->params().appendix()) {
1372 if (!layout.labelstring().empty())
1373 par->params().labelString(layout.labelstring());
1375 par->params().labelString(string());
1377 if (!layout.labelstring_appendix().empty())
1378 par->params().labelString(layout.labelstring_appendix());
1380 par->params().labelString(string());
1385 if (!par->params().appendix()) {
1386 switch (2 * LABEL_COUNTER_CHAPTER -
1387 textclass.maxcounter() + i) {
1388 case LABEL_COUNTER_CHAPTER:
1389 s << par->getCounter(i);
1391 case LABEL_COUNTER_SECTION:
1392 s << par->getCounter(i - 1) << '.'
1393 << par->getCounter(i);
1395 case LABEL_COUNTER_SUBSECTION:
1396 s << par->getCounter(i - 2) << '.'
1397 << par->getCounter(i - 1) << '.'
1398 << par->getCounter(i);
1400 case LABEL_COUNTER_SUBSUBSECTION:
1401 s << par->getCounter(i - 3) << '.'
1402 << par->getCounter(i - 2) << '.'
1403 << par->getCounter(i - 1) << '.'
1404 << par->getCounter(i);
1407 case LABEL_COUNTER_PARAGRAPH:
1408 s << par->getCounter(i - 4) << '.'
1409 << par->getCounter(i - 3) << '.'
1410 << par->getCounter(i - 2) << '.'
1411 << par->getCounter(i - 1) << '.'
1412 << par->getCounter(i);
1414 case LABEL_COUNTER_SUBPARAGRAPH:
1415 s << par->getCounter(i - 5) << '.'
1416 << par->getCounter(i - 4) << '.'
1417 << par->getCounter(i - 3) << '.'
1418 << par->getCounter(i - 2) << '.'
1419 << par->getCounter(i - 1) << '.'
1420 << par->getCounter(i);
1424 // Can this ever be reached? And in the
1425 // case it is, how can this be correct?
1427 s << par->getCounter(i) << '.';
1430 } else { // appendix
1431 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1432 case LABEL_COUNTER_CHAPTER:
1433 if (par->isRightToLeftPar(buf->params))
1434 s << hebrewCounter(par->getCounter(i));
1436 s << alphaCounter(par->getCounter(i));
1438 case LABEL_COUNTER_SECTION:
1439 if (par->isRightToLeftPar(buf->params))
1440 s << hebrewCounter(par->getCounter(i - 1));
1442 s << alphaCounter(par->getCounter(i - 1));
1445 << par->getCounter(i);
1448 case LABEL_COUNTER_SUBSECTION:
1449 if (par->isRightToLeftPar(buf->params))
1450 s << hebrewCounter(par->getCounter(i - 2));
1452 s << alphaCounter(par->getCounter(i - 2));
1455 << par->getCounter(i-1) << '.'
1456 << par->getCounter(i);
1459 case LABEL_COUNTER_SUBSUBSECTION:
1460 if (par->isRightToLeftPar(buf->params))
1461 s << hebrewCounter(par->getCounter(i-3));
1463 s << alphaCounter(par->getCounter(i-3));
1466 << par->getCounter(i-2) << '.'
1467 << par->getCounter(i-1) << '.'
1468 << par->getCounter(i);
1471 case LABEL_COUNTER_PARAGRAPH:
1472 if (par->isRightToLeftPar(buf->params))
1473 s << hebrewCounter(par->getCounter(i-4));
1475 s << alphaCounter(par->getCounter(i-4));
1478 << par->getCounter(i-3) << '.'
1479 << par->getCounter(i-2) << '.'
1480 << par->getCounter(i-1) << '.'
1481 << par->getCounter(i);
1484 case LABEL_COUNTER_SUBPARAGRAPH:
1485 if (par->isRightToLeftPar(buf->params))
1486 s << hebrewCounter(par->getCounter(i-5));
1488 s << alphaCounter(par->getCounter(i-5));
1491 << par->getCounter(i-4) << '.'
1492 << par->getCounter(i-3) << '.'
1493 << par->getCounter(i-2) << '.'
1494 << par->getCounter(i-1) << '.'
1495 << par->getCounter(i);
1499 // Can this ever be reached? And in the
1500 // case it is, how can this be correct?
1502 s << par->getCounter(i) << '.';
1508 par->params().labelString(par->params().labelString() +s.str().c_str());
1509 // We really want to remove the c_str as soon as
1512 for (i++; i < 10; ++i) {
1513 // reset the following counters
1514 par->setCounter(i, 0);
1516 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1517 for (i++; i < 10; ++i) {
1518 // reset the following counters
1519 par->setCounter(i, 0);
1521 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1522 par->incCounter(i + par->enumdepth);
1523 int number = par->getCounter(i + par->enumdepth);
1527 switch (par->enumdepth) {
1529 if (par->isRightToLeftPar(buf->params))
1531 << hebrewCounter(number)
1535 << loweralphaCounter(number)
1539 if (par->isRightToLeftPar(buf->params))
1540 s << '.' << romanCounter(number);
1542 s << romanCounter(number) << '.';
1545 if (par->isRightToLeftPar(buf->params))
1547 << alphaCounter(number);
1549 s << alphaCounter(number)
1553 if (par->isRightToLeftPar(buf->params))
1560 par->params().labelString(s.str().c_str());
1562 for (i += par->enumdepth + 1; i < 10; ++i) {
1563 // reset the following counters
1564 par->setCounter(i, 0);
1568 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1569 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1571 int number = par->getCounter(i);
1573 InsetCommandParams p("bibitem" );
1574 par->bibkey = new InsetBibKey(p);
1576 par->bibkey->setCounter(number);
1577 par->params().labelString(layout.labelstring());
1579 // In biblio should't be following counters but...
1581 string s = layout.labelstring();
1583 // the caption hack:
1584 if (layout.labeltype == LABEL_SENSITIVE) {
1585 bool isOK (par->inInset() && par->inInset()->owner() &&
1586 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1589 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1591 = floatList.getType(tmp->type());
1592 // We should get the correct number here too.
1593 s = fl.name() + " #:";
1595 /* par->SetLayout(0);
1596 s = layout->labelstring; */
1597 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1598 ? " :úåòîùî øñç" : "Senseless: ";
1601 par->params().labelString(s);
1603 /* reset the enumeration counter. They are always resetted
1604 * when there is any other layout between */
1605 for (int i = 6 + par->enumdepth; i < 10; ++i)
1606 par->setCounter(i, 0);
1611 // Updates all counters BEHIND the row. Changed paragraphs
1612 // with a dynamic left margin will be rebroken.
1613 void LyXText::updateCounters(BufferView * bview, Row * row) const
1621 par = row->par()->next();
1625 while (row->par() != par)
1628 setCounter(bview->buffer(), par);
1630 // now check for the headline layouts. remember that they
1631 // have a dynamic left margin
1632 LyXTextClass const & tclass =
1633 textclasslist[bview->buffer()->params.textclass];
1634 LyXLayout const & layout = tclass[par->layout()];
1636 if (layout.margintype == MARGIN_DYNAMIC
1637 || layout.labeltype == LABEL_SENSITIVE) {
1638 // Rebreak the paragraph
1639 removeParagraph(row);
1640 appendParagraph(bview, row);
1647 void LyXText::insertInset(BufferView * bview, Inset * inset)
1649 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1651 setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1653 cursor.par()->insertInset(cursor.pos(), inset);
1654 // Just to rebreak and refresh correctly.
1655 // The character will not be inserted a second time
1656 insertChar(bview, Paragraph::META_INSET);
1657 // If we enter a highly editable inset the cursor should be to before
1658 // the inset. This couldn't happen before as Undo was not handled inside
1659 // inset now after the Undo LyX tries to call inset->Edit(...) again
1660 // and cannot do this as the cursor is behind the inset and GetInset
1661 // does not return the inset!
1662 if (isHighlyEditableInset(inset)) {
1663 cursorLeft(bview, true);
1669 void LyXText::copyEnvironmentType()
1671 copylayouttype = cursor.par()->layout();
1675 void LyXText::pasteEnvironmentType(BufferView * bview)
1677 setLayout(bview, copylayouttype);
1681 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1683 // Stuff what we got on the clipboard. Even if there is no selection.
1685 // There is a problem with having the stuffing here in that the
1686 // larger the selection the slower LyX will get. This can be
1687 // solved by running the line below only when the selection has
1688 // finished. The solution used currently just works, to make it
1689 // faster we need to be more clever and probably also have more
1690 // calls to stuffClipboard. (Lgb)
1691 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1693 // This doesn't make sense, if there is no selection
1694 if (!selection.set())
1697 // OK, we have a selection. This is always between selection.start
1698 // and selection.end
1700 // make sure that the depth behind the selection are restored, too
1701 Paragraph * endpar = selection.end.par()->next();
1702 Paragraph * undoendpar = endpar;
1704 if (endpar && endpar->getDepth()) {
1705 while (endpar && endpar->getDepth()) {
1706 endpar = endpar->next();
1707 undoendpar = endpar;
1709 } else if (endpar) {
1710 endpar = endpar->next(); // because of parindents etc.
1713 setUndo(bview, Undo::DELETE,
1714 selection.start.par(), undoendpar);
1716 // there are two cases: cut only within one paragraph or
1717 // more than one paragraph
1718 if (selection.start.par() == selection.end.par()) {
1719 // only within one paragraph
1720 endpar = selection.end.par();
1721 int pos = selection.end.pos();
1722 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1723 selection.start.pos(), pos,
1724 bview->buffer()->params.textclass,
1726 selection.end.pos(pos);
1728 endpar = selection.end.par();
1729 int pos = selection.end.pos();
1730 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1731 selection.start.pos(), pos,
1732 bview->buffer()->params.textclass,
1735 selection.end.par(endpar);
1736 selection.end.pos(pos);
1737 cursor.pos(selection.end.pos());
1739 endpar = endpar->next();
1741 // sometimes necessary
1743 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1745 redoParagraphs(bview, selection.start, endpar);
1747 // cutSelection can invalidate the cursor so we need to set
1749 cursor = selection.start;
1751 // need a valid cursor. (Lgb)
1754 setCursor(bview, cursor.par(), cursor.pos());
1755 selection.cursor = cursor;
1756 updateCounters(bview, cursor.row());
1760 void LyXText::copySelection(BufferView * bview)
1762 // stuff the selection onto the X clipboard, from an explicit copy request
1763 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1765 // this doesnt make sense, if there is no selection
1766 if (!selection.set())
1769 // ok we have a selection. This is always between selection.start
1770 // and sel_end cursor
1772 // copy behind a space if there is one
1773 while (selection.start.par()->size() > selection.start.pos()
1774 && selection.start.par()->isLineSeparator(selection.start.pos())
1775 && (selection.start.par() != selection.end.par()
1776 || selection.start.pos() < selection.end.pos()))
1777 selection.start.pos(selection.start.pos() + 1);
1779 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1780 selection.start.pos(), selection.end.pos(),
1781 bview->buffer()->params.textclass);
1785 void LyXText::pasteSelection(BufferView * bview)
1787 // this does not make sense, if there is nothing to paste
1788 if (!CutAndPaste::checkPastePossible(cursor.par()))
1791 setUndo(bview, Undo::INSERT,
1792 cursor.par(), cursor.par()->next());
1795 Paragraph * actpar = cursor.par();
1796 int pos = cursor.pos();
1798 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1799 bview->buffer()->params.textclass);
1801 redoParagraphs(bview, cursor, endpar);
1803 setCursor(bview, cursor.par(), cursor.pos());
1806 selection.cursor = cursor;
1807 setCursor(bview, actpar, pos);
1808 setSelection(bview);
1809 updateCounters(bview, cursor.row());
1813 // sets the selection over the number of characters of string, no check!!
1814 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1819 selection.cursor = cursor;
1820 for (string::size_type i = 0; i < str.length(); ++i)
1822 setSelection(bview);
1826 // simple replacing. The font of the first selected character is used
1827 void LyXText::replaceSelectionWithString(BufferView * bview,
1830 setCursorParUndo(bview);
1833 if (!selection.set()) { // create a dummy selection
1834 selection.end = cursor;
1835 selection.start = cursor;
1838 // Get font setting before we cut
1839 pos_type pos = selection.end.pos();
1840 LyXFont const font = selection.start.par()
1841 ->getFontSettings(bview->buffer()->params,
1842 selection.start.pos());
1844 // Insert the new string
1845 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1846 selection.end.par()->insertChar(pos, (*cit), font);
1850 // Cut the selection
1851 cutSelection(bview, true, false);
1857 // needed to insert the selection
1858 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1860 Paragraph * par = cursor.par();
1861 pos_type pos = cursor.pos();
1862 Paragraph * endpar = cursor.par()->next();
1864 setCursorParUndo(bview);
1866 // only to be sure, should not be neccessary
1869 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1871 redoParagraphs(bview, cursor, endpar);
1872 setCursor(bview, cursor.par(), cursor.pos());
1873 selection.cursor = cursor;
1874 setCursor(bview, par, pos);
1875 setSelection(bview);
1879 // turns double-CR to single CR, others where converted into one
1880 // blank. Then InsertStringAsLines is called
1881 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1883 string linestr(str);
1884 bool newline_inserted = false;
1885 for (string::size_type i = 0; i < linestr.length(); ++i) {
1886 if (linestr[i] == '\n') {
1887 if (newline_inserted) {
1888 // we know that \r will be ignored by
1889 // InsertStringA. Of course, it is a dirty
1890 // trick, but it works...
1891 linestr[i - 1] = '\r';
1895 newline_inserted = true;
1897 } else if (IsPrintable(linestr[i])) {
1898 newline_inserted = false;
1901 insertStringAsLines(bview, linestr);
1905 bool LyXText::gotoNextInset(BufferView * bview,
1906 vector<Inset::Code> const & codes,
1907 string const & contents) const
1909 LyXCursor res = cursor;
1912 if (res.pos() < res.par()->size() - 1) {
1913 res.pos(res.pos() + 1);
1915 res.par(res.par()->next());
1919 } while (res.par() &&
1920 !(res.par()->isInset(res.pos())
1921 && (inset = res.par()->getInset(res.pos())) != 0
1922 && find(codes.begin(), codes.end(), inset->lyxCode())
1924 && (contents.empty() ||
1925 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1929 setCursor(bview, res.par(), res.pos(), false);
1936 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1939 LyXCursor tmpcursor;
1943 Row * row = getRow(par, pos, y);
1945 // is there a break one row above
1946 if (row->previous() && row->previous()->par() == row->par()) {
1947 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1948 if (z >= row->pos()) {
1949 // set the dimensions of the row above
1950 y -= row->previous()->height();
1952 refresh_row = row->previous();
1953 status(bview, LyXText::NEED_MORE_REFRESH);
1955 breakAgain(bview, row->previous());
1957 // set the cursor again. Otherwise
1958 // dangling pointers are possible
1959 setCursor(bview, cursor.par(), cursor.pos(),
1960 false, cursor.boundary());
1961 selection.cursor = cursor;
1966 int const tmpheight = row->height();
1967 pos_type const tmplast = rowLast(row);
1971 breakAgain(bview, row);
1972 if (row->height() == tmpheight && rowLast(row) == tmplast)
1973 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1975 status(bview, LyXText::NEED_MORE_REFRESH);
1977 // check the special right address boxes
1978 if (textclasslist[bview->buffer()->params.textclass][
1979 par->layout()].margintype
1980 == MARGIN_RIGHT_ADDRESS_BOX)
1988 redoDrawingOfParagraph(bview, tmpcursor);
1991 // set the cursor again. Otherwise dangling pointers are possible
1992 // also set the selection
1994 if (selection.set()) {
1996 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1997 false, selection.cursor.boundary());
1998 selection.cursor = cursor;
1999 setCursorIntern(bview, selection.start.par(),
2000 selection.start.pos(),
2001 false, selection.start.boundary());
2002 selection.start = cursor;
2003 setCursorIntern(bview, selection.end.par(),
2004 selection.end.pos(),
2005 false, selection.end.boundary());
2006 selection.end = cursor;
2007 setCursorIntern(bview, last_sel_cursor.par(),
2008 last_sel_cursor.pos(),
2009 false, last_sel_cursor.boundary());
2010 last_sel_cursor = cursor;
2013 setCursorIntern(bview, cursor.par(), cursor.pos(),
2014 false, cursor.boundary());
2018 // returns false if inset wasn't found
2019 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2021 // first check the current paragraph
2022 int pos = cursor.par()->getPositionOfInset(inset);
2024 checkParagraph(bview, cursor.par(), pos);
2028 // check every paragraph
2030 Paragraph * par = ownerParagraph();
2032 pos = par->getPositionOfInset(inset);
2034 checkParagraph(bview, par, pos);
2044 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2046 bool setfont, bool boundary) const
2048 LyXCursor old_cursor = cursor;
2049 setCursorIntern(bview, par, pos, setfont, boundary);
2050 return deleteEmptyParagraphMechanism(bview, old_cursor);
2054 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2055 pos_type pos, bool boundary) const
2062 cur.boundary(boundary);
2064 // get the cursor y position in text
2066 Row * row = getRow(par, pos, y);
2067 Row * old_row = row;
2069 // if we are before the first char of this row and are still in the
2070 // same paragraph and there is a previous row then put the cursor on
2071 // the end of the previous row
2072 cur.iy(y + row->baseline());
2074 if (row->previous() && pos &&
2075 par->getChar(pos) == Paragraph::META_INSET &&
2076 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2078 row = row->previous();
2083 // y is now the beginning of the cursor row
2084 y += row->baseline();
2085 // y is now the cursor baseline
2088 pos_type last = rowLastPrintable(old_row);
2090 if (pos > last + 1) {
2091 // This shouldn't happen.
2094 } else if (pos < row->pos()) {
2099 // now get the cursors x position
2100 float x = getCursorX(bview, row, pos, last, boundary);
2103 if (old_row != row) {
2104 x = getCursorX(bview, old_row, pos, last, boundary);
2111 float LyXText::getCursorX(BufferView * bview, Row * row,
2112 pos_type pos, pos_type last, bool boundary) const
2114 pos_type cursor_vpos = 0;
2116 float fill_separator;
2118 float fill_label_hfill;
2119 // This call HAS to be here because of the BidiTables!!!
2120 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2123 if (last < row->pos())
2124 cursor_vpos = row->pos();
2125 else if (pos > last && !boundary)
2126 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2127 ? row->pos() : last + 1;
2128 else if (pos > row->pos() &&
2129 (pos > last || boundary))
2130 /// Place cursor after char at (logical) position pos - 1
2131 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2132 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2134 /// Place cursor before char at (logical) position pos
2135 cursor_vpos = (bidi_level(pos) % 2 == 0)
2136 ? log2vis(pos) : log2vis(pos) + 1;
2138 pos_type main_body =
2139 beginningOfMainBody(bview->buffer(), row->par());
2140 if ((main_body > 0) &&
2141 ((main_body-1 > last) ||
2142 !row->par()->isLineSeparator(main_body-1)))
2145 for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2146 pos_type pos = vis2log(vpos);
2147 if (main_body > 0 && pos == main_body - 1) {
2148 x += fill_label_hfill +
2149 lyxfont::width(textclasslist[
2150 bview->buffer()->params.textclass][
2151 row->par()->layout()]
2153 getLabelFont(bview->buffer(), row->par()));
2154 if (row->par()->isLineSeparator(main_body-1))
2155 x -= singleWidth(bview, row->par(),main_body-1);
2157 if (hfillExpansion(bview->buffer(), row, pos)) {
2158 x += singleWidth(bview, row->par(), pos);
2159 if (pos >= main_body)
2162 x += fill_label_hfill;
2163 } else if (row->par()->isSeparator(pos)) {
2164 x += singleWidth(bview, row->par(), pos);
2165 if (pos >= main_body)
2166 x += fill_separator;
2168 x += singleWidth(bview, row->par(), pos);
2174 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2175 pos_type pos, bool setfont, bool boundary) const
2177 InsetText * it = static_cast<InsetText *>(par->inInset());
2179 if (it != inset_owner) {
2180 lyxerr[Debug::INSETS] << "InsetText is " << it
2182 << "inset_owner is "
2183 << inset_owner << endl;
2184 #ifdef WITH_WARNINGS
2185 #warning I believe this code is wrong. (Lgb)
2186 #warning Jürgen, have a look at this. (Lgb)
2187 #warning Hmmm, I guess you are right but we
2188 #warning should verify when this is needed
2190 // Jürgen, would you like to have a look?
2191 // I guess we need to move the outer cursor
2192 // and open and lock the inset (bla bla bla)
2193 // stuff I don't know... so can you have a look?
2195 // I moved the lyxerr stuff in here so we can see if
2196 // this is actually really needed and where!
2198 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2203 setCursor(bview, cursor, par, pos, boundary);
2205 setCurrentFont(bview);
2209 void LyXText::setCurrentFont(BufferView * bview) const
2211 pos_type pos = cursor.pos();
2212 if (cursor.boundary() && pos > 0)
2216 if (pos == cursor.par()->size())
2218 else // potentional bug... BUG (Lgb)
2219 if (cursor.par()->isSeparator(pos)) {
2220 if (pos > cursor.row()->pos() &&
2221 bidi_level(pos) % 2 ==
2222 bidi_level(pos - 1) % 2)
2224 else if (pos + 1 < cursor.par()->size())
2230 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2231 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2233 if (cursor.pos() == cursor.par()->size() &&
2234 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2235 !cursor.boundary()) {
2236 Language const * lang =
2237 cursor.par()->getParLanguage(bview->buffer()->params);
2238 current_font.setLanguage(lang);
2239 current_font.setNumber(LyXFont::OFF);
2240 real_current_font.setLanguage(lang);
2241 real_current_font.setNumber(LyXFont::OFF);
2246 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2248 LyXCursor old_cursor = cursor;
2250 setCursorFromCoordinates(bview, cursor, x, y);
2251 setCurrentFont(bview);
2252 deleteEmptyParagraphMechanism(bview, old_cursor);
2256 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2259 // Get the row first.
2261 Row * row = getRowNearY(y);
2263 pos_type const column = getColumnNearX(bview, row, x, bound);
2264 cur.par(row->par());
2265 cur.pos(row->pos() + column);
2267 cur.y(y + row->baseline());
2270 if (row->next() && row->next()->pos() == cur.pos() &&
2271 cur.par() == row->next()->par() &&
2272 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2273 (ins=cur.par()->getInset(cur.pos())) &&
2274 (ins->needFullRow() || ins->display()))
2276 // we enter here if we put the cursor on the end of the row before
2277 // a inset which uses a full row and in that case we HAVE to calculate
2278 // the right (i) values.
2279 pos_type last = rowLastPrintable(row);
2280 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2282 cur.iy(y + row->height() + row->next()->baseline());
2283 cur.irow(row->next());
2289 cur.boundary(bound);
2293 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2295 if (cursor.pos() > 0) {
2296 bool boundary = cursor.boundary();
2297 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2298 if (!internal && !boundary &&
2299 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2300 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2301 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2302 Paragraph * par = cursor.par()->previous();
2303 setCursor(bview, par, par->size());
2308 void LyXText::cursorRight(BufferView * bview, bool internal) const
2310 if (!internal && cursor.boundary() &&
2311 !cursor.par()->isNewline(cursor.pos()))
2312 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2313 else if (cursor.pos() < cursor.par()->size()) {
2314 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2316 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2317 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2318 } else if (cursor.par()->next())
2319 setCursor(bview, cursor.par()->next(), 0);
2323 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2326 int x = cursor.x_fix();
2327 int y = cursor.y() - cursor.row()->baseline() - 1;
2328 setCursorFromCoordinates(bview, x, y);
2330 int y1 = cursor.iy() - first_y;
2334 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2335 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2336 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2340 setCursorFromCoordinates(bview, cursor.x_fix(),
2341 cursor.y() - cursor.row()->baseline() - 1);
2346 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2349 int x = cursor.x_fix();
2350 int y = cursor.y() - cursor.row()->baseline() +
2351 cursor.row()->height() + 1;
2352 setCursorFromCoordinates(bview, x, y);
2353 if (!selecting && cursor.row() == cursor.irow()) {
2354 int y1 = cursor.iy() - first_y;
2358 bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2359 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2360 inset_hit->edit(bview, x, y - (y2 - y1), 0);
2364 setCursorFromCoordinates(bview, cursor.x_fix(),
2365 cursor.y() - cursor.row()->baseline()
2366 + cursor.row()->height() + 1);
2371 void LyXText::cursorUpParagraph(BufferView * bview) const
2373 if (cursor.pos() > 0) {
2374 setCursor(bview, cursor.par(), 0);
2376 else if (cursor.par()->previous()) {
2377 setCursor(bview, cursor.par()->previous(), 0);
2382 void LyXText::cursorDownParagraph(BufferView * bview) const
2384 if (cursor.par()->next()) {
2385 setCursor(bview, cursor.par()->next(), 0);
2387 setCursor(bview, cursor.par(), cursor.par()->size());
2391 // fix the cursor `cur' after a characters has been deleted at `where'
2392 // position. Called by deleteEmptyParagraphMechanism
2393 void LyXText::fixCursorAfterDelete(BufferView * bview,
2395 LyXCursor const & where) const
2397 // if cursor is not in the paragraph where the delete occured,
2399 if (cur.par() != where.par())
2402 // if cursor position is after the place where the delete occured,
2404 if (cur.pos() > where.pos())
2405 cur.pos(cur.pos()-1);
2407 // recompute row et al. for this cursor
2408 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2412 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2413 LyXCursor const & old_cursor) const
2415 // Would be wrong to delete anything if we have a selection.
2416 if (selection.set())
2419 // We allow all kinds of "mumbo-jumbo" when freespacing.
2420 if (textclasslist[bview->buffer()->params.textclass][
2421 old_cursor.par()->layout()].free_spacing
2422 || old_cursor.par()->isFreeSpacing())
2427 /* Ok I'll put some comments here about what is missing.
2428 I have fixed BackSpace (and thus Delete) to not delete
2429 double-spaces automagically. I have also changed Cut,
2430 Copy and Paste to hopefully do some sensible things.
2431 There are still some small problems that can lead to
2432 double spaces stored in the document file or space at
2433 the beginning of paragraphs. This happens if you have
2434 the cursor betwenn to spaces and then save. Or if you
2435 cut and paste and the selection have a space at the
2436 beginning and then save right after the paste. I am
2437 sure none of these are very hard to fix, but I will
2438 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2439 that I can get some feedback. (Lgb)
2442 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2443 // delete the LineSeparator.
2446 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2447 // delete the LineSeparator.
2450 // If the pos around the old_cursor were spaces, delete one of them.
2451 if (old_cursor.par() != cursor.par()
2452 || old_cursor.pos() != cursor.pos()) {
2453 // Only if the cursor has really moved
2455 if (old_cursor.pos() > 0
2456 && old_cursor.pos() < old_cursor.par()->size()
2457 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2458 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2459 old_cursor.par()->erase(old_cursor.pos() - 1);
2460 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2462 #ifdef WITH_WARNINGS
2463 #warning This will not work anymore when we have multiple views of the same buffer
2464 // In this case, we will have to correct also the cursors held by
2465 // other bufferviews. It will probably be easier to do that in a more
2466 // automated way in LyXCursor code. (JMarc 26/09/2001)
2468 // correct all cursors held by the LyXText
2469 fixCursorAfterDelete(bview, cursor, old_cursor);
2470 fixCursorAfterDelete(bview, selection.cursor,
2472 fixCursorAfterDelete(bview, selection.start,
2474 fixCursorAfterDelete(bview, selection.end, old_cursor);
2475 fixCursorAfterDelete(bview, last_sel_cursor,
2477 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2478 fixCursorAfterDelete(bview, toggle_end_cursor,
2484 // don't delete anything if this is the ONLY paragraph!
2485 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2488 // Do not delete empty paragraphs with keepempty set.
2490 [bview->buffer()->params.textclass]
2491 [old_cursor.par()->layout()].keepempty)
2494 // only do our magic if we changed paragraph
2495 if (old_cursor.par() == cursor.par())
2498 // record if we have deleted a paragraph
2499 // we can't possibly have deleted a paragraph before this point
2500 bool deleted = false;
2502 if ((old_cursor.par()->size() == 0
2503 || (old_cursor.par()->size() == 1
2504 && old_cursor.par()->isLineSeparator(0)))) {
2505 // ok, we will delete anything
2506 LyXCursor tmpcursor;
2508 // make sure that you do not delete any environments
2509 status(bview, LyXText::NEED_MORE_REFRESH);
2512 if (old_cursor.row()->previous()) {
2513 refresh_row = old_cursor.row()->previous();
2514 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2516 cursor = old_cursor; // that undo can restore the right cursor position
2517 Paragraph * endpar = old_cursor.par()->next();
2518 if (endpar && endpar->getDepth()) {
2519 while (endpar && endpar->getDepth()) {
2520 endpar = endpar->next();
2523 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2527 removeRow(old_cursor.row());
2528 if (ownerParagraph() == old_cursor.par()) {
2529 ownerParagraph(ownerParagraph()->next());
2532 delete old_cursor.par();
2534 /* Breakagain the next par. Needed because of
2535 * the parindent that can occur or dissappear.
2536 * The next row can change its height, if
2537 * there is another layout before */
2538 if (refresh_row->next()) {
2539 breakAgain(bview, refresh_row->next());
2540 updateCounters(bview, refresh_row);
2542 setHeightOfRow(bview, refresh_row);
2544 refresh_row = old_cursor.row()->next();
2545 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2548 cursor = old_cursor; // that undo can restore the right cursor position
2549 Paragraph * endpar = old_cursor.par()->next();
2550 if (endpar && endpar->getDepth()) {
2551 while (endpar && endpar->getDepth()) {
2552 endpar = endpar->next();
2555 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2559 removeRow(old_cursor.row());
2561 if (ownerParagraph() == old_cursor.par()) {
2562 ownerParagraph(ownerParagraph()->next());
2565 delete old_cursor.par();
2567 /* Breakagain the next par. Needed because of
2568 the parindent that can occur or dissappear.
2569 The next row can change its height, if
2570 there is another layout before */
2572 breakAgain(bview, refresh_row);
2573 updateCounters(bview, refresh_row->previous());
2578 setCursorIntern(bview, cursor.par(), cursor.pos());
2580 if (selection.cursor.par() == old_cursor.par()
2581 && selection.cursor.pos() == old_cursor.pos()) {
2582 // correct selection
2583 selection.cursor = cursor;
2587 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2588 redoParagraphs(bview, old_cursor,
2589 old_cursor.par()->next());
2591 setCursorIntern(bview, cursor.par(), cursor.pos());
2592 selection.cursor = cursor;
2599 void LyXText::toggleAppendix(BufferView * bview)
2601 Paragraph * par = cursor.par();
2602 bool start = !par->params().startOfAppendix();
2604 // ensure that we have only one start_of_appendix in this document
2605 Paragraph * tmp = ownerParagraph();
2606 for (; tmp; tmp = tmp->next()) {
2607 tmp->params().startOfAppendix(false);
2610 par->params().startOfAppendix(start);
2612 // we can set the refreshing parameters now
2613 status(bview, LyXText::NEED_MORE_REFRESH);
2615 refresh_row = 0; // not needed for full update
2616 updateCounters(bview, 0);
2617 setCursor(bview, cursor.par(), cursor.pos());
2621 Paragraph * LyXText::ownerParagraph() const
2624 return inset_owner->paragraph();
2626 return bv_owner->buffer()->paragraph;
2630 void LyXText::ownerParagraph(Paragraph * p) const
2633 inset_owner->paragraph(p);
2635 bv_owner->buffer()->paragraph = p;
2640 void LyXText::ownerParagraph(int id, Paragraph * p) const
2642 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2643 if (op && op->inInset()) {
2644 static_cast<InsetText *>(op->inInset())->paragraph(p);
2651 LyXText::text_status LyXText::status() const
2657 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2659 // well as much as I know && binds more then || so the above and the
2660 // below are identical (this for your known use of parentesis!)
2661 // Now some explanation:
2662 // We should only go up with refreshing code so this means that if
2663 // we have a MORE refresh we should never set it to LITTLE if we still
2664 // didn't handle it (and then it will be UNCHANGED. Now as long as
2665 // we stay inside one LyXText this may work but we need to tell the
2666 // outermost LyXText that it should REALLY draw us if there is some
2667 // change in a Inset::LyXText. So you see that when we are inside a
2668 // inset's LyXText we give the LITTLE to the outermost LyXText to
2669 // tell'em that it should redraw the actual row (where the inset
2670 // resides! Capito?!
2672 if ((status_ != NEED_MORE_REFRESH)
2673 || (status_ == NEED_MORE_REFRESH
2674 && st != NEED_VERY_LITTLE_REFRESH))
2677 if (inset_owner && st != UNCHANGED) {
2678 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2679 if (!bview->text->refresh_row) {
2680 bview->text->refresh_row = bview->text->cursor.row();
2681 bview->text->refresh_y = bview->text->cursor.y() -
2682 bview->text->cursor.row()->baseline();