1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
14 #pragma implementation "lyxtext.h"
19 #include "paragraph.h"
20 #include "insets/inseterror.h"
21 #include "insets/insetbib.h"
22 #include "insets/insetspecialchar.h"
23 #include "insets/insettext.h"
24 #include "insets/insetfloat.h"
27 #include "support/textutils.h"
28 #include "support/lstrings.h"
29 #include "undo_funcs.h"
31 #include "bufferparams.h"
32 #include "lyx_gui_misc.h"
34 #include "BufferView.h"
36 #include "CutAndPaste.h"
42 #include "FloatList.h"
44 #include "ParagraphParameters.h"
45 #include "support/LAssert.h"
54 LyXText::LyXText(BufferView * bv)
55 : number_of_rows(0), height(0), width(0), first(0),
56 bv_owner(bv), inset_owner(0), the_locking_inset(0),
57 need_break_row(0), refresh_y(0), refresh_row(0),
58 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
63 LyXText::LyXText(InsetText * inset)
64 : number_of_rows(0), height(0), width(0), first(0),
65 bv_owner(0), inset_owner(inset), the_locking_inset(0),
66 need_break_row(0), refresh_y(0), refresh_row(0),
67 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
71 void LyXText::init(BufferView * bview, bool reinit)
74 // Delete all rows, this does not touch the paragraphs!
75 Row * tmprow = firstrow;
77 tmprow = firstrow->next();
81 lastrow = refresh_row = need_break_row = 0;
82 width = height = copylayouttype = 0;
83 number_of_rows = first = refresh_y = 0;
84 status_ = LyXText::UNCHANGED;
88 Paragraph * par = ownerParagraph();
89 current_font = getFont(bview->buffer(), par, 0);
91 insertParagraph(bview, par, lastrow);
94 setCursorIntern(bview, firstrow->par(), 0);
95 selection.cursor = cursor;
101 // Delete all rows, this does not touch the paragraphs!
102 Row * tmprow = firstrow;
104 tmprow = firstrow->next();
113 LyXFont const realizeFont(LyXFont const & font,
117 LyXFont tmpfont(font);
118 Paragraph::depth_type par_depth = par->getDepth();
120 // Resolve against environment font information
121 while (par && par_depth && !tmpfont.resolved()) {
122 par = par->outerHook();
124 #ifndef INHERIT_LANGUAGE
125 tmpfont.realize(textclasslist.
126 Style(buf->params.textclass,
127 par->getLayout()).font);
129 tmpfont.realize(textclasslist.
130 Style(buf->params.textclass,
131 par->getLayout()).font,
132 buf->params.language);
134 par_depth = par->getDepth();
138 #ifndef INHERIT_LANGUAGE
139 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
141 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
142 buf->params.language);
151 // Gets the fully instantiated font at a given position in a paragraph
152 // Basically the same routine as Paragraph::getFont() in paragraph.C.
153 // The difference is that this one is used for displaying, and thus we
154 // are allowed to make cosmetic improvements. For instance make footnotes
156 // If position is -1, we get the layout font of the paragraph.
157 // If position is -2, we get the font of the manual label of the paragraph.
158 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
161 lyx::Assert(pos >= 0);
163 LyXLayout const & layout =
164 textclasslist.Style(buf->params.textclass, par->getLayout());
166 Paragraph::depth_type par_depth = par->getDepth();
167 // We specialize the 95% common case:
169 if (layout.labeltype == LABEL_MANUAL
170 && pos < beginningOfMainBody(buf, par)) {
172 LyXFont f = par->getFontSettings(buf->params,
174 #ifndef INHERIT_LANGUAGE
175 return f.realize(layout.reslabelfont);
177 return f.realize(layout.reslabelfont, buf->params.language);
180 LyXFont f = par->getFontSettings(buf->params, pos);
181 #ifndef INHERIT_LANGUAGE
182 return f.realize(layout.resfont);
184 return f.realize(layout.resfont, buf->params.language);
189 // The uncommon case need not be optimized as much
193 if (pos < beginningOfMainBody(buf, par)) {
195 layoutfont = layout.labelfont;
198 layoutfont = layout.font;
201 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
202 #ifndef INHERIT_LANGUAGE
203 tmpfont.realize(layoutfont);
205 tmpfont.realize(layoutfont, buf->params.language);
208 return realizeFont(tmpfont, buf, par);
212 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
214 LyXLayout const & layout =
215 textclasslist.Style(buf->params.textclass, par->getLayout());
217 Paragraph::depth_type par_depth = par->getDepth();
220 return layout.resfont;
223 return realizeFont(layout.font, buf, par);
227 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
229 LyXLayout const & layout =
230 textclasslist.Style(buf->params.textclass, par->getLayout());
232 Paragraph::depth_type par_depth = par->getDepth();
235 return layout.reslabelfont;
238 return realizeFont(layout.labelfont, buf, par);
242 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
243 pos_type pos, LyXFont const & fnt,
246 Buffer const * buf = bv->buffer();
247 LyXFont font = getFont(buf, par, pos);
248 font.update(fnt, buf->params.language, toggleall);
249 // Let the insets convert their font
250 if (par->isInset(pos)) {
251 Inset * inset = par->getInset(pos);
252 if (isEditableInset(inset)) {
253 UpdatableInset * uinset =
254 static_cast<UpdatableInset *>(inset);
255 uinset->setFont(bv, fnt, toggleall, true);
259 LyXLayout const & layout =
260 textclasslist.Style(buf->params.textclass,
263 // Get concrete layout font to reduce against
266 if (pos < beginningOfMainBody(buf, par))
267 layoutfont = layout.labelfont;
269 layoutfont = layout.font;
271 // Realize against environment font information
272 if (par->getDepth()){
273 Paragraph * tp = par;
274 while (!layoutfont.resolved() && tp && tp->getDepth()) {
275 tp = tp->outerHook();
277 #ifndef INHERIT_LANGUAGE
278 layoutfont.realize(textclasslist.
279 Style(buf->params.textclass,
280 tp->getLayout()).font);
282 layoutfont.realize(textclasslist.
283 Style(buf->params.textclass,
284 tp->getLayout()).font,
285 buf->params.language);
290 #ifndef INHERIT_LANGUAGE
291 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
293 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
294 buf->params.language);
297 // Now, reduce font against full layout font
298 font.reduce(layoutfont);
300 par->setFont(pos, font);
304 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
305 pos_type pos, LyXFont const & fnt)
309 LyXLayout const & layout =
310 textclasslist.Style(buf->params.textclass,
313 // Get concrete layout font to reduce against
316 if (pos < beginningOfMainBody(buf, par))
317 layoutfont = layout.labelfont;
319 layoutfont = layout.font;
321 // Realize against environment font information
322 if (par->getDepth()){
323 Paragraph * tp = par;
324 while (!layoutfont.resolved() && tp && tp->getDepth()) {
325 tp = tp->outerHook();
327 #ifndef INHERIT_LANGUAGE
328 layoutfont.realize(textclasslist.
329 Style(buf->params.textclass,
330 tp->getLayout()).font);
332 layoutfont.realize(textclasslist.
333 Style(buf->params.textclass,
334 tp->getLayout()).font,
335 buf->params.language);
340 #ifndef INHERIT_LANGUAGE
341 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
343 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
344 buf->params.language);
347 // Now, reduce font against full layout font
348 font.reduce(layoutfont);
350 par->setFont(pos, font);
354 // inserts a new row behind the specified row, increments
355 // the touched counters
356 void LyXText::insertRow(Row * row, Paragraph * par,
359 Row * tmprow = new Row;
362 tmprow->next(firstrow);
365 tmprow->previous(row);
366 tmprow->next(row->next());
371 tmprow->next()->previous(tmprow);
373 if (tmprow->previous())
374 tmprow->previous()->next(tmprow);
386 // removes the row and reset the touched counters
387 void LyXText::removeRow(Row * row) const
390 row->next()->previous(row->previous());
391 if (!row->previous()) {
392 firstrow = row->next();
394 row->previous()->next(row->next());
397 lastrow = row->previous();
399 height -= row->height(); // the text becomes smaller
402 --number_of_rows; // one row less
406 // remove all following rows of the paragraph of the specified row.
407 void LyXText::removeParagraph(Row * row) const
409 Paragraph * tmppar = row->par();
413 while (row && row->par() == tmppar) {
414 tmprow = row->next();
421 // insert the specified paragraph behind the specified row
422 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
425 insertRow(row, par, 0); /* insert a new row, starting
428 setCounter(bview->buffer(), par); // set the counters
430 // and now append the whole paragraph behind the new row
433 appendParagraph(bview, firstrow);
435 row->next()->height(0);
436 appendParagraph(bview, row->next());
441 Inset * LyXText::getInset() const
444 if (cursor.pos() == 0 && cursor.par()->bibkey) {
445 inset = cursor.par()->bibkey;
446 } else if (cursor.pos() < cursor.par()->size()
447 && cursor.par()->isInset(cursor.pos())) {
448 inset = cursor.par()->getInset(cursor.pos());
454 void LyXText::toggleInset(BufferView * bview)
456 Inset * inset = getInset();
457 if (!isEditableInset(inset))
459 //bview->owner()->message(inset->editMessage());
461 // do we want to keep this?? (JMarc)
462 if (!isHighlyEditableInset(inset))
463 setCursorParUndo(bview);
465 if (inset->isOpen()) {
471 inset->open(bview, !inset->isOpen());
476 /* used in setlayout */
477 // Asger is not sure we want to do this...
478 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
481 LyXLayout const & layout =
482 textclasslist.Style(buf->params.textclass, par->getLayout());
485 for (pos_type pos = 0; pos < par->size(); ++pos) {
486 if (pos < beginningOfMainBody(buf, par))
487 layoutfont = layout.labelfont;
489 layoutfont = layout.font;
491 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
492 tmpfont.reduce(layoutfont);
493 par->setFont(pos, tmpfont);
498 Paragraph * LyXText::setLayout(BufferView * bview,
499 LyXCursor & cur, LyXCursor & sstart_cur,
500 LyXCursor & send_cur,
503 Paragraph * endpar = send_cur.par()->next();
504 Paragraph * undoendpar = endpar;
506 if (endpar && endpar->getDepth()) {
507 while (endpar && endpar->getDepth()) {
508 endpar = endpar->next();
512 endpar = endpar->next(); // because of parindents etc.
515 setUndo(bview, Undo::EDIT,
516 sstart_cur.par(), undoendpar);
518 // ok we have a selection. This is always between sstart_cur
519 // and sel_end cursor
522 LyXLayout const & lyxlayout =
523 textclasslist.Style(bview->buffer()->params.textclass, layout);
525 while (cur.par() != send_cur.par()) {
526 cur.par()->setLayout(layout);
527 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
528 Paragraph * fppar = cur.par();
529 fppar->params().spaceTop(lyxlayout.fill_top ?
530 VSpace(VSpace::VFILL)
531 : VSpace(VSpace::NONE));
532 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
533 VSpace(VSpace::VFILL)
534 : VSpace(VSpace::NONE));
535 if (lyxlayout.margintype == MARGIN_MANUAL)
536 cur.par()->setLabelWidthString(lyxlayout.labelstring());
537 if (lyxlayout.labeltype != LABEL_BIBLIO
539 delete fppar->bibkey;
542 cur.par(cur.par()->next());
544 cur.par()->setLayout(layout);
545 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
546 Paragraph * fppar = cur.par();
547 fppar->params().spaceTop(lyxlayout.fill_top ?
548 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
549 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
550 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
551 if (lyxlayout.margintype == MARGIN_MANUAL)
552 cur.par()->setLabelWidthString(lyxlayout.labelstring());
553 if (lyxlayout.labeltype != LABEL_BIBLIO
555 delete fppar->bibkey;
562 // set layout over selection and make a total rebreak of those paragraphs
563 void LyXText::setLayout(BufferView * bview, layout_type layout)
565 LyXCursor tmpcursor = cursor; /* store the current cursor */
567 // if there is no selection just set the layout
568 // of the current paragraph */
569 if (!selection.set()) {
570 selection.start = cursor; // dummy selection
571 selection.end = cursor;
573 Paragraph * endpar = setLayout(bview, cursor, selection.start,
574 selection.end, layout);
575 redoParagraphs(bview, selection.start, endpar);
577 // we have to reset the selection, because the
578 // geometry could have changed
579 setCursor(bview, selection.start.par(),
580 selection.start.pos(), false);
581 selection.cursor = cursor;
582 setCursor(bview, selection.end.par(), selection.end.pos(), false);
583 updateCounters(bview, cursor.row());
586 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
590 // increment depth over selection and
591 // make a total rebreak of those paragraphs
592 void LyXText::incDepth(BufferView * bview)
594 // If there is no selection, just use the current paragraph
595 if (!selection.set()) {
596 selection.start = cursor; // dummy selection
597 selection.end = cursor;
600 // We end at the next paragraph with depth 0
601 Paragraph * endpar = selection.end.par()->next();
603 Paragraph * undoendpar = endpar;
605 if (endpar && endpar->getDepth()) {
606 while (endpar && endpar->getDepth()) {
607 endpar = endpar->next();
611 endpar = endpar->next(); // because of parindents etc.
614 setUndo(bview, Undo::EDIT,
615 selection.start.par(), undoendpar);
617 LyXCursor tmpcursor = cursor; // store the current cursor
619 // ok we have a selection. This is always between sel_start_cursor
620 // and sel_end cursor
621 cursor = selection.start;
623 bool anything_changed = false;
626 // NOTE: you can't change the depth of a bibliography entry
628 textclasslist.Style(bview->buffer()->params.textclass,
629 cursor.par()->getLayout()
630 ).labeltype != LABEL_BIBLIO) {
631 Paragraph * prev = cursor.par()->previous();
634 && (prev->getDepth() - cursor.par()->getDepth() > 0
635 || (prev->getDepth() == cursor.par()->getDepth()
636 && textclasslist.Style(bview->buffer()->params.textclass,
637 prev->getLayout()).isEnvironment()))) {
638 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
639 anything_changed = true;
642 if (cursor.par() == selection.end.par())
644 cursor.par(cursor.par()->next());
647 // if nothing changed set all depth to 0
648 if (!anything_changed) {
649 cursor = selection.start;
650 while (cursor.par() != selection.end.par()) {
651 cursor.par()->params().depth(0);
652 cursor.par(cursor.par()->next());
654 cursor.par()->params().depth(0);
657 redoParagraphs(bview, selection.start, endpar);
659 // we have to reset the selection, because the
660 // geometry could have changed
661 setCursor(bview, selection.start.par(), selection.start.pos());
662 selection.cursor = cursor;
663 setCursor(bview, selection.end.par(), selection.end.pos());
664 updateCounters(bview, cursor.row());
667 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
671 // decrement depth over selection and
672 // make a total rebreak of those paragraphs
673 void LyXText::decDepth(BufferView * bview)
675 // if there is no selection just set the layout
676 // of the current paragraph
677 if (!selection.set()) {
678 selection.start = cursor; // dummy selection
679 selection.end = cursor;
681 Paragraph * endpar = selection.end.par()->next();
682 Paragraph * undoendpar = endpar;
684 if (endpar && endpar->getDepth()) {
685 while (endpar && endpar->getDepth()) {
686 endpar = endpar->next();
690 endpar = endpar->next(); // because of parindents etc.
693 setUndo(bview, Undo::EDIT,
694 selection.start.par(), undoendpar);
696 LyXCursor tmpcursor = cursor; // store the current cursor
698 // ok we have a selection. This is always between sel_start_cursor
699 // and sel_end cursor
700 cursor = selection.start;
703 if (cursor.par()->params().depth()) {
704 cursor.par()->params()
705 .depth(cursor.par()->params().depth() - 1);
707 if (cursor.par() == selection.end.par()) {
710 cursor.par(cursor.par()->next());
713 redoParagraphs(bview, selection.start, endpar);
715 // we have to reset the selection, because the
716 // geometry could have changed
717 setCursor(bview, selection.start.par(),
718 selection.start.pos());
719 selection.cursor = cursor;
720 setCursor(bview, selection.end.par(), selection.end.pos());
721 updateCounters(bview, cursor.row());
724 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
728 // set font over selection and make a total rebreak of those paragraphs
729 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
731 // if there is no selection just set the current_font
732 if (!selection.set()) {
733 // Determine basis font
735 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
737 layoutfont = getLabelFont(bview->buffer(),
740 layoutfont = getLayoutFont(bview->buffer(),
743 // Update current font
744 real_current_font.update(font,
745 bview->buffer()->params.language,
748 // Reduce to implicit settings
749 current_font = real_current_font;
750 current_font.reduce(layoutfont);
751 // And resolve it completely
752 #ifndef INHERIT_LANGUAGE
753 real_current_font.realize(layoutfont);
755 real_current_font.realize(layoutfont,
756 bview->buffer()->params.language);
761 LyXCursor tmpcursor = cursor; // store the current cursor
763 // ok we have a selection. This is always between sel_start_cursor
764 // and sel_end cursor
766 setUndo(bview, Undo::EDIT,
767 selection.start.par(), selection.end.par()->next());
769 cursor = selection.start;
770 while (cursor.par() != selection.end.par() ||
771 (cursor.pos() < selection.end.pos()))
773 if (cursor.pos() < cursor.par()->size()) {
774 // an open footnote should behave
776 setCharFont(bview, cursor.par(), cursor.pos(),
778 cursor.pos(cursor.pos() + 1);
781 cursor.par(cursor.par()->next());
786 redoParagraphs(bview, selection.start, selection.end.par()->next());
788 // we have to reset the selection, because the
789 // geometry could have changed
790 setCursor(bview, selection.start.par(), selection.start.pos());
791 selection.cursor = cursor;
792 setCursor(bview, selection.end.par(), selection.end.pos());
795 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
796 tmpcursor.boundary());
800 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
802 Row * tmprow = cur.row();
803 int y = cur.y() - tmprow->baseline();
805 setHeightOfRow(bview, tmprow);
807 while (tmprow->previous()
808 && tmprow->previous()->par() == tmprow->par()) {
809 tmprow = tmprow->previous();
810 y -= tmprow->height();
811 setHeightOfRow(bview, tmprow);
814 // we can set the refreshing parameters now
815 status(bview, LyXText::NEED_MORE_REFRESH);
817 refresh_row = tmprow;
818 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
822 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
824 Row * tmprow = cur.row();
826 int y = cur.y() - tmprow->baseline();
827 setHeightOfRow(bview, tmprow);
829 while (tmprow->previous()
830 && tmprow->previous()->par() == tmprow->par()) {
831 tmprow = tmprow->previous();
832 y -= tmprow->height();
835 // we can set the refreshing parameters now
836 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
838 refresh_row = tmprow;
840 status(bview, LyXText::NEED_MORE_REFRESH);
841 setCursor(bview, cur.par(), cur.pos());
845 // deletes and inserts again all paragaphs between the cursor
846 // and the specified par
847 // This function is needed after SetLayout and SetFont etc.
848 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
849 Paragraph const * endpar) const
852 Paragraph * tmppar = 0;
853 Paragraph * first_phys_par = 0;
855 Row * tmprow = cur.row();
857 int y = cur.y() - tmprow->baseline();
859 if (!tmprow->previous()) {
860 // a trick/hack for UNDO
861 // Can somebody please tell me _why_ this solves
863 first_phys_par = firstParagraph();
865 first_phys_par = tmprow->par();
866 while (tmprow->previous()
867 && tmprow->previous()->par() == first_phys_par)
869 tmprow = tmprow->previous();
870 y -= tmprow->height();
874 // we can set the refreshing parameters now
875 status(bview, LyXText::NEED_MORE_REFRESH);
877 refresh_row = tmprow->previous(); /* the real refresh row will
878 be deleted, so I store
882 tmppar = tmprow->next()->par();
885 while (tmppar != endpar) {
886 removeRow(tmprow->next());
888 tmppar = tmprow->next()->par();
893 // remove the first one
894 tmprow2 = tmprow; /* this is because tmprow->previous()
896 tmprow = tmprow->previous();
899 tmppar = first_phys_par;
903 insertParagraph(bview, tmppar, tmprow);
907 while (tmprow->next()
908 && tmprow->next()->par() == tmppar) {
909 tmprow = tmprow->next();
911 tmppar = tmppar->next();
913 } while (tmppar && tmppar != endpar);
915 // this is because of layout changes
917 refresh_y -= refresh_row->height();
918 setHeightOfRow(bview, refresh_row);
920 refresh_row = firstrow;
922 setHeightOfRow(bview, refresh_row);
925 if (tmprow && tmprow->next())
926 setHeightOfRow(bview, tmprow->next());
930 bool LyXText::fullRebreak(BufferView * bview)
936 if (need_break_row) {
937 breakAgain(bview, need_break_row);
945 // important for the screen
948 /* the cursor set functions have a special mechanism. When they
949 * realize, that you left an empty paragraph, they will delete it.
950 * They also delete the corresponding row */
952 // need the selection cursor:
953 void LyXText::setSelection(BufferView * bview)
955 bool const lsel = selection.set();
957 if (!selection.set()) {
958 last_sel_cursor = selection.cursor;
959 selection.start = selection.cursor;
960 selection.end = selection.cursor;
965 // first the toggling area
966 if (cursor.y() < last_sel_cursor.y()
967 || (cursor.y() == last_sel_cursor.y()
968 && cursor.x() < last_sel_cursor.x())) {
969 toggle_end_cursor = last_sel_cursor;
970 toggle_cursor = cursor;
972 toggle_end_cursor = cursor;
973 toggle_cursor = last_sel_cursor;
976 last_sel_cursor = cursor;
978 // and now the whole selection
980 if (selection.cursor.par() == cursor.par())
981 if (selection.cursor.pos() < cursor.pos()) {
982 selection.end = cursor;
983 selection.start = selection.cursor;
985 selection.end = selection.cursor;
986 selection.start = cursor;
988 else if (selection.cursor.y() < cursor.y() ||
989 (selection.cursor.y() == cursor.y()
990 && selection.cursor.x() < cursor.x())) {
991 selection.end = cursor;
992 selection.start = selection.cursor;
995 selection.end = selection.cursor;
996 selection.start = cursor;
999 // a selection with no contents is not a selection
1000 if (selection.start.par() == selection.end.par() &&
1001 selection.start.pos() == selection.end.pos())
1002 selection.set(false);
1004 if (inset_owner && (selection.set() || lsel))
1005 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1009 string const LyXText::selectionAsString(Buffer const * buffer,
1012 if (!selection.set()) return string();
1015 // Special handling if the whole selection is within one paragraph
1016 if (selection.start.par() == selection.end.par()) {
1017 result += selection.start.par()->asString(buffer,
1018 selection.start.pos(),
1019 selection.end.pos(),
1024 // The selection spans more than one paragraph
1026 // First paragraph in selection
1027 result += selection.start.par()->asString(buffer,
1028 selection.start.pos(),
1029 selection.start.par()->size(),
1033 // The paragraphs in between (if any)
1034 LyXCursor tmpcur(selection.start);
1035 tmpcur.par(tmpcur.par()->next());
1036 while (tmpcur.par() != selection.end.par()) {
1037 result += tmpcur.par()->asString(buffer, 0,
1038 tmpcur.par()->size(),
1040 tmpcur.par(tmpcur.par()->next());
1043 // Last paragraph in selection
1044 result += selection.end.par()->asString(buffer, 0,
1045 selection.end.pos(), label);
1051 void LyXText::clearSelection() const
1053 selection.set(false);
1054 selection.mark(false);
1055 selection.end = selection.start = selection.cursor = cursor;
1059 void LyXText::cursorHome(BufferView * bview) const
1061 setCursor(bview, cursor.par(), cursor.row()->pos());
1065 void LyXText::cursorEnd(BufferView * bview) const
1067 if (!cursor.row()->next()
1068 || cursor.row()->next()->par() != cursor.row()->par()) {
1069 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1071 if (cursor.par()->size() &&
1072 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1073 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1074 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1076 setCursor(bview,cursor.par(),
1077 rowLast(cursor.row()) + 1);
1083 void LyXText::cursorTop(BufferView * bview) const
1085 while (cursor.par()->previous())
1086 cursor.par(cursor.par()->previous());
1087 setCursor(bview, cursor.par(), 0);
1091 void LyXText::cursorBottom(BufferView * bview) const
1093 while (cursor.par()->next())
1094 cursor.par(cursor.par()->next());
1095 setCursor(bview, cursor.par(), cursor.par()->size());
1099 void LyXText::toggleFree(BufferView * bview,
1100 LyXFont const & font, bool toggleall)
1102 // If the mask is completely neutral, tell user
1103 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1104 // Could only happen with user style
1105 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1109 // Try implicit word selection
1110 // If there is a change in the language the implicit word selection
1112 LyXCursor resetCursor = cursor;
1113 bool implicitSelection = (font.language() == ignore_language
1114 && font.number() == LyXFont::IGNORE)
1115 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1118 setFont(bview, font, toggleall);
1120 // Implicit selections are cleared afterwards
1121 //and cursor is set to the original position.
1122 if (implicitSelection) {
1124 cursor = resetCursor;
1125 setCursor(bview, cursor.par(), cursor.pos());
1126 selection.cursor = cursor;
1129 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1133 string LyXText::getStringToIndex(BufferView * bview)
1137 // Try implicit word selection
1138 // If there is a change in the language the implicit word selection
1140 LyXCursor resetCursor = cursor;
1141 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1143 if (!selection.set()) {
1144 bview->owner()->message(_("Nothing to index!"));
1147 if (selection.start.par() != selection.end.par()) {
1148 bview->owner()->message(_("Cannot index more than one paragraph!"));
1152 idxstring = selectionAsString(bview->buffer(), false);
1154 // Implicit selections are cleared afterwards
1155 //and cursor is set to the original position.
1156 if (implicitSelection) {
1158 cursor = resetCursor;
1159 setCursor(bview, cursor.par(), cursor.pos());
1160 selection.cursor = cursor;
1166 LyXText::pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1167 Paragraph const * par) const
1169 if (textclasslist.Style(buf->params.textclass,
1170 par->getLayout()).labeltype != LABEL_MANUAL)
1173 return par->beginningOfMainBody();
1177 /* the DTP switches for paragraphs. LyX will store them in the
1178 * first physicla paragraph. When a paragraph is broken, the top settings
1179 * rest, the bottom settings are given to the new one. So I can make shure,
1180 * they do not duplicate themself and you cannnot make dirty things with
1183 void LyXText::setParagraph(BufferView * bview,
1184 bool line_top, bool line_bottom,
1185 bool pagebreak_top, bool pagebreak_bottom,
1186 VSpace const & space_top,
1187 VSpace const & space_bottom,
1188 Spacing const & spacing,
1190 string labelwidthstring,
1193 LyXCursor tmpcursor = cursor;
1194 if (!selection.set()) {
1195 selection.start = cursor;
1196 selection.end = cursor;
1199 // make sure that the depth behind the selection are restored, too
1200 Paragraph * endpar = selection.end.par()->next();
1201 Paragraph * undoendpar = endpar;
1203 if (endpar && endpar->getDepth()) {
1204 while (endpar && endpar->getDepth()) {
1205 endpar = endpar->next();
1206 undoendpar = endpar;
1210 // because of parindents etc.
1211 endpar = endpar->next();
1214 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1217 Paragraph * tmppar = selection.end.par();
1218 while (tmppar != selection.start.par()->previous()) {
1219 setCursor(bview, tmppar, 0);
1220 status(bview, LyXText::NEED_MORE_REFRESH);
1221 refresh_row = cursor.row();
1222 refresh_y = cursor.y() - cursor.row()->baseline();
1223 cursor.par()->params().lineTop(line_top);
1224 cursor.par()->params().lineBottom(line_bottom);
1225 cursor.par()->params().pagebreakTop(pagebreak_top);
1226 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1227 cursor.par()->params().spaceTop(space_top);
1228 cursor.par()->params().spaceBottom(space_bottom);
1229 cursor.par()->params().spacing(spacing);
1230 // does the layout allow the new alignment?
1231 if (align == LYX_ALIGN_LAYOUT)
1232 align = textclasslist
1233 .Style(bview->buffer()->params.textclass,
1234 cursor.par()->getLayout()).align;
1235 if (align & textclasslist
1236 .Style(bview->buffer()->params.textclass,
1237 cursor.par()->getLayout()).alignpossible) {
1238 if (align == textclasslist
1239 .Style(bview->buffer()->params.textclass,
1240 cursor.par()->getLayout()).align)
1241 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1243 cursor.par()->params().align(align);
1245 cursor.par()->setLabelWidthString(labelwidthstring);
1246 cursor.par()->params().noindent(noindent);
1247 tmppar = cursor.par()->previous();
1250 redoParagraphs(bview, selection.start, endpar);
1253 setCursor(bview, selection.start.par(), selection.start.pos());
1254 selection.cursor = cursor;
1255 setCursor(bview, selection.end.par(), selection.end.pos());
1256 setSelection(bview);
1257 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1259 bview->updateInset(inset_owner, true);
1263 char loweralphaCounter(int n)
1265 if (n < 1 || n > 26)
1275 char alphaCounter(int n)
1277 if (n < 1 || n > 26)
1285 char hebrewCounter(int n)
1287 static const char hebrew[22] = {
1288 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1289 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1290 '÷', 'ø', 'ù', 'ú'
1292 if (n < 1 || n > 22)
1300 string const romanCounter(int n)
1302 static char const * roman[20] = {
1303 "i", "ii", "iii", "iv", "v",
1304 "vi", "vii", "viii", "ix", "x",
1305 "xi", "xii", "xiii", "xiv", "xv",
1306 "xvi", "xvii", "xviii", "xix", "xx"
1308 if (n < 1 || n > 20)
1317 // set the counter of a paragraph. This includes the labels
1318 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1320 LyXLayout const & layout =
1321 textclasslist.Style(buf->params.textclass,
1324 LyXTextClass const & textclass =
1325 textclasslist.TextClass(buf->params.textclass);
1327 // copy the prev-counters to this one,
1328 // unless this is the first paragraph
1329 if (par->previous()) {
1330 for (int i = 0; i < 10; ++i) {
1331 par->setCounter(i, par->previous()->getFirstCounter(i));
1333 par->params().appendix(par->previous()->params().appendix());
1334 if (!par->params().appendix() && par->params().startOfAppendix()) {
1335 par->params().appendix(true);
1336 for (int i = 0; i < 10; ++i) {
1337 par->setCounter(i, 0);
1340 par->enumdepth = par->previous()->enumdepth;
1341 par->itemdepth = par->previous()->itemdepth;
1343 for (int i = 0; i < 10; ++i) {
1344 par->setCounter(i, 0);
1346 par->params().appendix(par->params().startOfAppendix());
1351 /* Maybe we have to increment the enumeration depth.
1352 * BUT, enumeration in a footnote is considered in isolation from its
1353 * surrounding paragraph so don't increment if this is the
1354 * first line of the footnote
1355 * AND, bibliographies can't have their depth changed ie. they
1356 * are always of depth 0
1359 && par->previous()->getDepth() < par->getDepth()
1360 && textclasslist.Style(buf->params.textclass,
1361 par->previous()->getLayout()
1362 ).labeltype == LABEL_COUNTER_ENUMI
1363 && par->enumdepth < 3
1364 && layout.labeltype != LABEL_BIBLIO) {
1368 // Maybe we have to decrement the enumeration depth, see note above
1370 && par->previous()->getDepth() > par->getDepth()
1371 && layout.labeltype != LABEL_BIBLIO) {
1372 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1373 par->setCounter(6 + par->enumdepth,
1374 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1375 /* reset the counters.
1376 * A depth change is like a breaking layout
1378 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1379 par->setCounter(i, 0);
1382 if (!par->params().labelString().empty()) {
1383 par->params().labelString(string());
1386 if (layout.margintype == MARGIN_MANUAL) {
1387 if (par->params().labelWidthString().empty()) {
1388 par->setLabelWidthString(layout.labelstring());
1391 par->setLabelWidthString(string());
1394 // is it a layout that has an automatic label?
1395 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1397 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1398 if (i >= 0 && i<= buf->params.secnumdepth) {
1399 par->incCounter(i); // increment the counter
1401 // Is there a label? Useful for Chapter layout
1402 if (!par->params().appendix()) {
1403 if (!layout.labelstring().empty())
1404 par->params().labelString(layout.labelstring());
1406 par->params().labelString(string());
1408 if (!layout.labelstring_appendix().empty())
1409 par->params().labelString(layout.labelstring_appendix());
1411 par->params().labelString(string());
1416 if (!par->params().appendix()) {
1417 switch (2 * LABEL_COUNTER_CHAPTER -
1418 textclass.maxcounter() + i) {
1419 case LABEL_COUNTER_CHAPTER:
1420 s << par->getCounter(i);
1422 case LABEL_COUNTER_SECTION:
1423 s << par->getCounter(i - 1) << '.'
1424 << par->getCounter(i);
1426 case LABEL_COUNTER_SUBSECTION:
1427 s << par->getCounter(i - 2) << '.'
1428 << par->getCounter(i - 1) << '.'
1429 << par->getCounter(i);
1431 case LABEL_COUNTER_SUBSUBSECTION:
1432 s << par->getCounter(i - 3) << '.'
1433 << par->getCounter(i - 2) << '.'
1434 << par->getCounter(i - 1) << '.'
1435 << par->getCounter(i);
1438 case LABEL_COUNTER_PARAGRAPH:
1439 s << par->getCounter(i - 4) << '.'
1440 << par->getCounter(i - 3) << '.'
1441 << par->getCounter(i - 2) << '.'
1442 << par->getCounter(i - 1) << '.'
1443 << par->getCounter(i);
1445 case LABEL_COUNTER_SUBPARAGRAPH:
1446 s << par->getCounter(i - 5) << '.'
1447 << par->getCounter(i - 4) << '.'
1448 << par->getCounter(i - 3) << '.'
1449 << par->getCounter(i - 2) << '.'
1450 << par->getCounter(i - 1) << '.'
1451 << par->getCounter(i);
1455 // Can this ever be reached? And in the
1456 // case it is, how can this be correct?
1458 s << par->getCounter(i) << '.';
1461 } else { // appendix
1462 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1463 case LABEL_COUNTER_CHAPTER:
1464 if (par->isRightToLeftPar(buf->params))
1465 s << hebrewCounter(par->getCounter(i));
1467 s << alphaCounter(par->getCounter(i));
1469 case LABEL_COUNTER_SECTION:
1470 if (par->isRightToLeftPar(buf->params))
1471 s << hebrewCounter(par->getCounter(i - 1));
1473 s << alphaCounter(par->getCounter(i - 1));
1476 << par->getCounter(i);
1479 case LABEL_COUNTER_SUBSECTION:
1480 if (par->isRightToLeftPar(buf->params))
1481 s << hebrewCounter(par->getCounter(i - 2));
1483 s << alphaCounter(par->getCounter(i - 2));
1486 << par->getCounter(i-1) << '.'
1487 << par->getCounter(i);
1490 case LABEL_COUNTER_SUBSUBSECTION:
1491 if (par->isRightToLeftPar(buf->params))
1492 s << hebrewCounter(par->getCounter(i-3));
1494 s << alphaCounter(par->getCounter(i-3));
1497 << par->getCounter(i-2) << '.'
1498 << par->getCounter(i-1) << '.'
1499 << par->getCounter(i);
1502 case LABEL_COUNTER_PARAGRAPH:
1503 if (par->isRightToLeftPar(buf->params))
1504 s << hebrewCounter(par->getCounter(i-4));
1506 s << alphaCounter(par->getCounter(i-4));
1509 << par->getCounter(i-3) << '.'
1510 << par->getCounter(i-2) << '.'
1511 << par->getCounter(i-1) << '.'
1512 << par->getCounter(i);
1515 case LABEL_COUNTER_SUBPARAGRAPH:
1516 if (par->isRightToLeftPar(buf->params))
1517 s << hebrewCounter(par->getCounter(i-5));
1519 s << alphaCounter(par->getCounter(i-5));
1522 << par->getCounter(i-4) << '.'
1523 << par->getCounter(i-3) << '.'
1524 << par->getCounter(i-2) << '.'
1525 << par->getCounter(i-1) << '.'
1526 << par->getCounter(i);
1530 // Can this ever be reached? And in the
1531 // case it is, how can this be correct?
1533 s << par->getCounter(i) << '.';
1539 par->params().labelString(par->params().labelString() +s.str().c_str());
1540 // We really want to remove the c_str as soon as
1543 for (i++; i < 10; ++i) {
1544 // reset the following counters
1545 par->setCounter(i, 0);
1547 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1548 for (i++; i < 10; ++i) {
1549 // reset the following counters
1550 par->setCounter(i, 0);
1552 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1553 par->incCounter(i + par->enumdepth);
1554 int number = par->getCounter(i + par->enumdepth);
1558 switch (par->enumdepth) {
1560 if (par->isRightToLeftPar(buf->params))
1562 << hebrewCounter(number)
1566 << loweralphaCounter(number)
1570 if (par->isRightToLeftPar(buf->params))
1571 s << '.' << romanCounter(number);
1573 s << romanCounter(number) << '.';
1576 if (par->isRightToLeftPar(buf->params))
1578 << alphaCounter(number);
1580 s << alphaCounter(number)
1584 if (par->isRightToLeftPar(buf->params))
1591 par->params().labelString(s.str().c_str());
1593 for (i += par->enumdepth + 1; i < 10; ++i) {
1594 // reset the following counters
1595 par->setCounter(i, 0);
1599 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1600 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1602 int number = par->getCounter(i);
1604 InsetCommandParams p( "bibitem" );
1605 par->bibkey = new InsetBibKey(p);
1607 par->bibkey->setCounter(number);
1608 par->params().labelString(layout.labelstring());
1610 // In biblio should't be following counters but...
1612 string s = layout.labelstring();
1614 // the caption hack:
1615 if (layout.labeltype == LABEL_SENSITIVE) {
1616 bool isOK (par->inInset() && par->inInset()->owner() &&
1617 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1620 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1622 = floatList.getType(tmp->type());
1623 // We should get the correct number here too.
1624 s = fl.name() + " #:";
1626 /* par->SetLayout(0);
1627 s = layout->labelstring; */
1628 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1629 ? " :úåòîùî øñç" : "Senseless: ";
1632 par->params().labelString(s);
1634 /* reset the enumeration counter. They are always resetted
1635 * when there is any other layout between */
1636 for (int i = 6 + par->enumdepth; i < 10; ++i)
1637 par->setCounter(i, 0);
1642 // Updates all counters BEHIND the row. Changed paragraphs
1643 // with a dynamic left margin will be rebroken.
1644 void LyXText::updateCounters(BufferView * bview, Row * row) const
1652 par = row->par()->next();
1656 while (row->par() != par)
1659 setCounter(bview->buffer(), par);
1661 // now check for the headline layouts. remember that they
1662 // have a dynamic left margin
1663 if ((textclasslist.Style(bview->buffer()->params.textclass,
1664 par->layout).margintype == MARGIN_DYNAMIC
1665 || textclasslist.Style(bview->buffer()->params.textclass,
1666 par->layout).labeltype == LABEL_SENSITIVE)) {
1668 // Rebreak the paragraph
1669 removeParagraph(row);
1670 appendParagraph(bview, row);
1677 void LyXText::insertInset(BufferView * bview, Inset * inset)
1679 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1681 setUndo(bview, Undo::INSERT,
1682 cursor.par(), cursor.par()->next());
1683 cursor.par()->insertInset(cursor.pos(), inset);
1684 // Just to rebreak and refresh correctly.
1685 // The character will not be inserted a second time
1686 insertChar(bview, Paragraph::META_INSET);
1688 // If we enter a highly editable inset the cursor should be to before
1689 // the inset. This couldn't happen before as Undo was not handled inside
1690 // inset now after the Undo LyX tries to call inset->Edit(...) again
1691 // and cannot do this as the cursor is behind the inset and GetInset
1692 // does not return the inset!
1693 if (isHighlyEditableInset(inset)) {
1694 cursorLeft(bview, true);
1700 void LyXText::copyEnvironmentType()
1702 copylayouttype = cursor.par()->getLayout();
1706 void LyXText::pasteEnvironmentType(BufferView * bview)
1708 setLayout(bview, copylayouttype);
1712 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1714 // Stuff what we got on the clipboard. Even if there is no selection.
1716 // There is a problem with having the stuffing here in that the
1717 // larger the selection the slower LyX will get. This can be
1718 // solved by running the line below only when the selection has
1719 // finished. The solution used currently just works, to make it
1720 // faster we need to be more clever and probably also have more
1721 // calls to stuffClipboard. (Lgb)
1722 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1724 // This doesn't make sense, if there is no selection
1725 if (!selection.set())
1728 // OK, we have a selection. This is always between selection.start
1729 // and selection.end
1731 // make sure that the depth behind the selection are restored, too
1732 Paragraph * endpar = selection.end.par()->next();
1733 Paragraph * undoendpar = endpar;
1735 if (endpar && endpar->getDepth()) {
1736 while (endpar && endpar->getDepth()) {
1737 endpar = endpar->next();
1738 undoendpar = endpar;
1740 } else if (endpar) {
1741 endpar = endpar->next(); // because of parindents etc.
1744 setUndo(bview, Undo::DELETE,
1745 selection.start.par(), undoendpar);
1747 // there are two cases: cut only within one paragraph or
1748 // more than one paragraph
1749 if (selection.start.par() == selection.end.par()) {
1750 // only within one paragraph
1751 endpar = selection.end.par();
1752 int pos = selection.end.pos();
1753 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1754 selection.start.pos(), pos,
1755 bview->buffer()->params.textclass,
1757 selection.end.pos(pos);
1759 endpar = selection.end.par();
1760 int pos = selection.end.pos();
1761 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1762 selection.start.pos(), pos,
1763 bview->buffer()->params.textclass,
1766 selection.end.par(endpar);
1767 selection.end.pos(pos);
1768 cursor.pos(selection.end.pos());
1770 endpar = endpar->next();
1772 // sometimes necessary
1774 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1776 redoParagraphs(bview, selection.start, endpar);
1778 // cutSelection can invalidate the cursor so we need to set
1780 cursor = selection.start;
1782 // need a valid cursor. (Lgb)
1785 setCursor(bview, cursor.par(), cursor.pos());
1786 selection.cursor = cursor;
1787 updateCounters(bview, cursor.row());
1791 void LyXText::copySelection(BufferView * bview)
1793 // Stuff what we got on the clipboard. Even if there is no selection.
1795 // There is a problem with having the stuffing here in that the
1796 // larger the selection the slower LyX will get. This can be
1797 // solved by running the line below only when the selection has
1798 // finished. The solution used currently just works, to make it
1799 // faster we need to be more clever and probably also have more
1800 // calls to stuffClipboard. (Lgb)
1801 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1803 // this doesnt make sense, if there is no selection
1804 if (!selection.set())
1807 // ok we have a selection. This is always between selection.start
1808 // and sel_end cursor
1810 // copy behind a space if there is one
1811 while (selection.start.par()->size() > selection.start.pos()
1812 && selection.start.par()->isLineSeparator(selection.start.pos())
1813 && (selection.start.par() != selection.end.par()
1814 || selection.start.pos() < selection.end.pos()))
1815 selection.start.pos(selection.start.pos() + 1);
1817 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1818 selection.start.pos(), selection.end.pos(),
1819 bview->buffer()->params.textclass);
1823 void LyXText::pasteSelection(BufferView * bview)
1825 // this does not make sense, if there is nothing to paste
1826 if (!CutAndPaste::checkPastePossible(cursor.par()))
1829 setUndo(bview, Undo::INSERT,
1830 cursor.par(), cursor.par()->next());
1833 Paragraph * actpar = cursor.par();
1834 int pos = cursor.pos();
1836 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1837 bview->buffer()->params.textclass);
1839 redoParagraphs(bview, cursor, endpar);
1841 setCursor(bview, cursor.par(), cursor.pos());
1844 setCursor(bview, actpar, pos);
1845 updateCounters(bview, cursor.row());
1849 // returns a pointer to the very first Paragraph
1850 Paragraph * LyXText::firstParagraph() const
1852 return ownerParagraph();
1856 // sets the selection over the number of characters of string, no check!!
1857 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1862 selection.cursor = cursor;
1863 for (string::size_type i = 0; i < str.length(); ++i)
1865 setSelection(bview);
1869 // simple replacing. The font of the first selected character is used
1870 void LyXText::replaceSelectionWithString(BufferView * bview,
1873 setCursorParUndo(bview);
1876 if (!selection.set()) { // create a dummy selection
1877 selection.end = cursor;
1878 selection.start = cursor;
1881 // Get font setting before we cut
1882 pos_type pos = selection.end.pos();
1883 LyXFont const font = selection.start.par()
1884 ->getFontSettings(bview->buffer()->params,
1885 selection.start.pos());
1887 // Insert the new string
1888 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1889 selection.end.par()->insertChar(pos, (*cit), font);
1893 // Cut the selection
1894 cutSelection(bview, true, false);
1900 // needed to insert the selection
1901 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1903 Paragraph * par = cursor.par();
1904 pos_type pos = cursor.pos();
1905 Paragraph * endpar = cursor.par()->next();
1907 setCursorParUndo(bview);
1909 // only to be sure, should not be neccessary
1912 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1914 redoParagraphs(bview, cursor, endpar);
1915 setCursor(bview, cursor.par(), cursor.pos());
1916 selection.cursor = cursor;
1917 setCursor(bview, par, pos);
1918 setSelection(bview);
1922 // turns double-CR to single CR, others where converted into one
1923 // blank. Then InsertStringAsLines is called
1924 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1926 string linestr(str);
1927 bool newline_inserted = false;
1928 for (string::size_type i = 0; i < linestr.length(); ++i) {
1929 if (linestr[i] == '\n') {
1930 if (newline_inserted) {
1931 // we know that \r will be ignored by
1932 // InsertStringA. Of course, it is a dirty
1933 // trick, but it works...
1934 linestr[i - 1] = '\r';
1938 newline_inserted = true;
1940 } else if (IsPrintable(linestr[i])) {
1941 newline_inserted = false;
1944 insertStringAsLines(bview, linestr);
1948 bool LyXText::gotoNextInset(BufferView * bview,
1949 std::vector<Inset::Code> const & codes,
1950 string const & contents) const
1952 LyXCursor res = cursor;
1955 if (res.pos() < res.par()->size() - 1) {
1956 res.pos(res.pos() + 1);
1958 res.par(res.par()->next());
1962 } while (res.par() &&
1963 !(res.par()->isInset(res.pos())
1964 && (inset = res.par()->getInset(res.pos())) != 0
1965 && find(codes.begin(), codes.end(), inset->lyxCode())
1967 && (contents.empty() ||
1968 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1972 setCursor(bview, res.par(), res.pos(), false);
1979 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1982 LyXCursor tmpcursor;
1986 Row * row = getRow(par, pos, y);
1988 // is there a break one row above
1989 if (row->previous() && row->previous()->par() == row->par()) {
1990 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1991 if (z >= row->pos()) {
1992 // set the dimensions of the row above
1993 y -= row->previous()->height();
1995 refresh_row = row->previous();
1996 status(bview, LyXText::NEED_MORE_REFRESH);
1998 breakAgain(bview, row->previous());
2000 // set the cursor again. Otherwise
2001 // dangling pointers are possible
2002 setCursor(bview, cursor.par(), cursor.pos(),
2003 false, cursor.boundary());
2004 selection.cursor = cursor;
2009 int const tmpheight = row->height();
2010 pos_type const tmplast = rowLast(row);
2014 breakAgain(bview, row);
2015 if (row->height() == tmpheight && rowLast(row) == tmplast)
2016 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2018 status(bview, LyXText::NEED_MORE_REFRESH);
2020 // check the special right address boxes
2021 if (textclasslist.Style(bview->buffer()->params.textclass,
2022 par->getLayout()).margintype
2023 == MARGIN_RIGHT_ADDRESS_BOX)
2031 redoDrawingOfParagraph(bview, tmpcursor);
2034 // set the cursor again. Otherwise dangling pointers are possible
2035 // also set the selection
2037 if (selection.set()) {
2039 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2040 false, selection.cursor.boundary());
2041 selection.cursor = cursor;
2042 setCursorIntern(bview, selection.start.par(),
2043 selection.start.pos(),
2044 false, selection.start.boundary());
2045 selection.start = cursor;
2046 setCursorIntern(bview, selection.end.par(),
2047 selection.end.pos(),
2048 false, selection.end.boundary());
2049 selection.end = cursor;
2050 setCursorIntern(bview, last_sel_cursor.par(),
2051 last_sel_cursor.pos(),
2052 false, last_sel_cursor.boundary());
2053 last_sel_cursor = cursor;
2056 setCursorIntern(bview, cursor.par(), cursor.pos(),
2057 false, cursor.boundary());
2061 // returns false if inset wasn't found
2062 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2064 // first check the current paragraph
2065 int pos = cursor.par()->getPositionOfInset(inset);
2067 checkParagraph(bview, cursor.par(), pos);
2071 // check every paragraph
2073 Paragraph * par = firstParagraph();
2075 pos = par->getPositionOfInset(inset);
2077 checkParagraph(bview, par, pos);
2087 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2089 bool setfont, bool boundary) const
2091 LyXCursor old_cursor = cursor;
2092 setCursorIntern(bview, par, pos, setfont, boundary);
2093 deleteEmptyParagraphMechanism(bview, old_cursor);
2097 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2098 pos_type pos, bool boundary) const
2102 cur.boundary(boundary);
2104 // get the cursor y position in text
2106 Row * row = getRow(par, pos, y);
2107 // y is now the beginning of the cursor row
2108 y += row->baseline();
2109 // y is now the cursor baseline
2112 // now get the cursors x position
2114 float fill_separator;
2116 float fill_label_hfill;
2117 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2119 pos_type cursor_vpos = 0;
2120 pos_type last = rowLastPrintable(row);
2122 if (pos > last + 1) // This shouldn't happen.
2124 else if (pos < row->pos())
2127 if (last < row->pos())
2128 cursor_vpos = row->pos();
2129 else if (pos > last && !boundary)
2130 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2131 ? row->pos() : last + 1;
2132 else if (pos > row->pos() &&
2133 (pos > last || boundary))
2134 /// Place cursor after char at (logical) position pos - 1
2135 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2136 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2138 /// Place cursor before char at (logical) position pos
2139 cursor_vpos = (bidi_level(pos) % 2 == 0)
2140 ? log2vis(pos) : log2vis(pos) + 1;
2142 pos_type main_body =
2143 beginningOfMainBody(bview->buffer(), row->par());
2144 if ((main_body > 0) &&
2145 ((main_body-1 > last) ||
2146 !row->par()->isLineSeparator(main_body-1)))
2149 for (pos_type vpos = row->pos();
2150 vpos < cursor_vpos; ++vpos) {
2151 pos = vis2log(vpos);
2152 if (main_body > 0 && pos == main_body - 1) {
2153 x += fill_label_hfill +
2154 lyxfont::width(textclasslist.Style(
2155 bview->buffer()->params.textclass,
2156 row->par()->getLayout())
2158 getLabelFont(bview->buffer(), row->par()));
2159 if (row->par()->isLineSeparator(main_body-1))
2160 x -= singleWidth(bview, row->par(),main_body-1);
2162 if (hfillExpansion(bview->buffer(), row, pos)) {
2163 x += singleWidth(bview, row->par(), pos);
2164 if (pos >= main_body)
2167 x += fill_label_hfill;
2168 } else if (row->par()->isSeparator(pos)) {
2169 x += singleWidth(bview, row->par(), pos);
2170 if (pos >= main_body)
2171 x += fill_separator;
2173 x += singleWidth(bview, row->par(), pos);
2182 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2184 bool setfont, bool boundary) const
2186 InsetText * it = static_cast<InsetText *>(par->inInset());
2188 if (it != inset_owner) {
2189 lyxerr << "InsetText is " << it << endl;
2190 lyxerr << "inset_owner is " << inset_owner << endl;
2191 #warning I believe this code is wrong. (Lgb)
2192 #warning Jürgen, have a look at this. (Lgb)
2193 #warning Hmmm, I guess you are right but we
2194 #warning should verify when this is needed
2195 // Jürgen, would you like to have a look?
2196 // I guess we need to move the outer cursor
2197 // and open and lock the inset (bla bla bla)
2198 // stuff I don't know... so can you have a look?
2200 // I moved the lyxerr stuff in here so we can see if
2201 // this is actually really needed and where!
2203 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2209 setCursor(bview, cursor, par, pos, boundary);
2211 setCurrentFont(bview);
2215 void LyXText::setCurrentFont(BufferView * bview) const
2217 pos_type pos = cursor.pos();
2218 if (cursor.boundary() && pos > 0)
2222 if (pos == cursor.par()->size())
2224 else // potentional bug... BUG (Lgb)
2225 if (cursor.par()->isSeparator(pos)) {
2226 if (pos > cursor.row()->pos() &&
2227 bidi_level(pos) % 2 ==
2228 bidi_level(pos - 1) % 2)
2230 else if (pos + 1 < cursor.par()->size())
2236 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2237 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2239 if (cursor.pos() == cursor.par()->size() &&
2240 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2241 !cursor.boundary()) {
2242 Language const * lang =
2243 cursor.par()->getParLanguage(bview->buffer()->params);
2244 current_font.setLanguage(lang);
2245 current_font.setNumber(LyXFont::OFF);
2246 real_current_font.setLanguage(lang);
2247 real_current_font.setNumber(LyXFont::OFF);
2252 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2254 LyXCursor old_cursor = cursor;
2256 setCursorFromCoordinates(bview, cursor, x, y);
2257 setCurrentFont(bview);
2258 deleteEmptyParagraphMechanism(bview, old_cursor);
2262 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2265 // Get the row first.
2267 Row * row = getRowNearY(y);
2269 pos_type const column = getColumnNearX(bview, row, x, bound);
2270 cur.par(row->par());
2271 cur.pos(row->pos() + column);
2273 cur.y(y + row->baseline());
2275 cur.boundary(bound);
2279 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2281 if (cursor.pos() > 0) {
2282 bool boundary = cursor.boundary();
2283 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2284 if (!internal && !boundary &&
2285 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2286 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2287 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2288 Paragraph * par = cursor.par()->previous();
2289 setCursor(bview, par, par->size());
2294 void LyXText::cursorRight(BufferView * bview, bool internal) const
2296 if (!internal && cursor.boundary() &&
2297 !cursor.par()->isNewline(cursor.pos()))
2298 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2299 else if (cursor.pos() < cursor.par()->size()) {
2300 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2302 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2303 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2304 } else if (cursor.par()->next())
2305 setCursor(bview, cursor.par()->next(), 0);
2309 void LyXText::cursorUp(BufferView * bview) const
2311 setCursorFromCoordinates(bview, cursor.x_fix(),
2312 cursor.y() - cursor.row()->baseline() - 1);
2316 void LyXText::cursorDown(BufferView * bview) const
2318 setCursorFromCoordinates(bview, cursor.x_fix(),
2319 cursor.y() - cursor.row()->baseline()
2320 + cursor.row()->height() + 1);
2324 void LyXText::cursorUpParagraph(BufferView * bview) const
2326 if (cursor.pos() > 0) {
2327 setCursor(bview, cursor.par(), 0);
2329 else if (cursor.par()->previous()) {
2330 setCursor(bview, cursor.par()->previous(), 0);
2335 void LyXText::cursorDownParagraph(BufferView * bview) const
2337 if (cursor.par()->next()) {
2338 setCursor(bview, cursor.par()->next(), 0);
2340 setCursor(bview, cursor.par(), cursor.par()->size());
2344 // fix the cursor `cur' after a characters has been deleted at `where'
2345 // position. Called by deleteEmptyParagraphMechanism
2346 void LyXText::fixCursorAfterDelete(BufferView * bview,
2348 LyXCursor const & where) const
2350 // if cursor is not in the paragraph where the delete occured,
2352 if (cur.par() != where.par())
2355 // if cursor position is after the place where the delete occured,
2357 if (cur.pos() > where.pos())
2358 cur.pos(cur.pos()-1);
2360 // recompute row et al. for this cursor
2361 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2365 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2366 LyXCursor const & old_cursor) const
2368 // Would be wrong to delete anything if we have a selection.
2369 if (selection.set()) return;
2371 // We allow all kinds of "mumbo-jumbo" when freespacing.
2372 if (textclasslist.Style(bview->buffer()->params.textclass,
2373 old_cursor.par()->getLayout()).free_spacing)
2376 bool deleted = false;
2378 /* Ok I'll put some comments here about what is missing.
2379 I have fixed BackSpace (and thus Delete) to not delete
2380 double-spaces automagically. I have also changed Cut,
2381 Copy and Paste to hopefully do some sensible things.
2382 There are still some small problems that can lead to
2383 double spaces stored in the document file or space at
2384 the beginning of paragraphs. This happens if you have
2385 the cursor betwenn to spaces and then save. Or if you
2386 cut and paste and the selection have a space at the
2387 beginning and then save right after the paste. I am
2388 sure none of these are very hard to fix, but I will
2389 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2390 that I can get some feedback. (Lgb)
2393 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2394 // delete the LineSeparator.
2397 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2398 // delete the LineSeparator.
2401 // If the pos around the old_cursor were spaces, delete one of them.
2402 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2403 // Only if the cursor has really moved
2405 if (old_cursor.pos() > 0
2406 && old_cursor.pos() < old_cursor.par()->size()
2407 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2408 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2409 old_cursor.par()->erase(old_cursor.pos() - 1);
2410 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2412 #ifdef WITH_WARNINGS
2413 #warning This will not work anymore when we have multiple views of the same buffer
2414 // In this case, we will have to correct also the cursors held by
2415 // other bufferviews. It will probably be easier to do that in a more
2416 // automated way in LyXCursor code. (JMarc 26/09/2001)
2418 // correct all cursors held by the LyXText
2419 fixCursorAfterDelete(bview, cursor, old_cursor);
2420 fixCursorAfterDelete(bview, selection.cursor,
2422 fixCursorAfterDelete(bview, selection.start,
2424 fixCursorAfterDelete(bview, selection.end, old_cursor);
2425 fixCursorAfterDelete(bview, last_sel_cursor,
2427 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2428 fixCursorAfterDelete(bview, toggle_end_cursor,
2434 // Do not delete empty paragraphs with keepempty set.
2435 if ((textclasslist.Style(bview->buffer()->params.textclass,
2436 old_cursor.par()->getLayout())).keepempty)
2439 // only do our magic if we changed paragraph
2440 if (old_cursor.par() == cursor.par())
2443 if ((old_cursor.par()->size() == 0
2444 || (old_cursor.par()->size() == 1
2445 && old_cursor.par()->isLineSeparator(0)))) {
2446 // ok, we will delete anything
2447 LyXCursor tmpcursor;
2449 // make sure that you do not delete any environments
2450 status(bview, LyXText::NEED_MORE_REFRESH);
2453 if (old_cursor.row()->previous()) {
2454 refresh_row = old_cursor.row()->previous();
2455 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2457 cursor = old_cursor; // that undo can restore the right cursor position
2458 Paragraph * endpar = old_cursor.par()->next();
2459 if (endpar && endpar->getDepth()) {
2460 while (endpar && endpar->getDepth()) {
2461 endpar = endpar->next();
2464 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2468 removeRow(old_cursor.row());
2469 if (ownerParagraph() == old_cursor.par()) {
2470 ownerParagraph(ownerParagraph()->next());
2473 delete old_cursor.par();
2475 /* Breakagain the next par. Needed because of
2476 * the parindent that can occur or dissappear.
2477 * The next row can change its height, if
2478 * there is another layout before */
2479 if (refresh_row->next()) {
2480 breakAgain(bview, refresh_row->next());
2481 updateCounters(bview, refresh_row);
2483 setHeightOfRow(bview, refresh_row);
2485 refresh_row = old_cursor.row()->next();
2486 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2489 cursor = old_cursor; // that undo can restore the right cursor position
2490 Paragraph * endpar = old_cursor.par()->next();
2491 if (endpar && endpar->getDepth()) {
2492 while (endpar && endpar->getDepth()) {
2493 endpar = endpar->next();
2496 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2500 removeRow(old_cursor.row());
2502 if (ownerParagraph() == old_cursor.par()) {
2503 ownerParagraph(ownerParagraph()->next());
2506 delete old_cursor.par();
2508 /* Breakagain the next par. Needed because of
2509 the parindent that can occur or dissappear.
2510 The next row can change its height, if
2511 there is another layout before */
2513 breakAgain(bview, refresh_row);
2514 updateCounters(bview, refresh_row->previous());
2519 setCursorIntern(bview, cursor.par(), cursor.pos());
2521 if (selection.cursor.par() == old_cursor.par()
2522 && selection.cursor.pos() == selection.cursor.pos()) {
2523 // correct selection
2524 selection.cursor = cursor;
2528 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2529 redoParagraphs(bview, old_cursor,
2530 old_cursor.par()->next());
2532 setCursorIntern(bview, cursor.par(), cursor.pos());
2533 selection.cursor = cursor;
2539 void LyXText::toggleAppendix(BufferView * bview)
2541 Paragraph * par = cursor.par();
2542 bool start = !par->params().startOfAppendix();
2544 // ensure that we have only one start_of_appendix in this document
2545 Paragraph * tmp = firstParagraph();
2546 for (; tmp; tmp = tmp->next()) {
2547 tmp->params().startOfAppendix(false);
2550 par->params().startOfAppendix(start);
2552 // we can set the refreshing parameters now
2553 status(bview, LyXText::NEED_MORE_REFRESH);
2555 refresh_row = 0; // not needed for full update
2556 updateCounters(bview, 0);
2557 setCursor(bview, cursor.par(), cursor.pos());
2561 Paragraph * LyXText::ownerParagraph() const
2564 return inset_owner->paragraph();
2566 return bv_owner->buffer()->paragraph;
2570 void LyXText::ownerParagraph(Paragraph * p) const
2573 inset_owner->paragraph(p);
2575 bv_owner->buffer()->paragraph = p;
2580 void LyXText::ownerParagraph(int id, Paragraph * p) const
2582 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2583 if (op && op->inInset()) {
2584 static_cast<InsetText *>(op->inInset())->paragraph(p);
2587 inset_owner->paragraph(p);
2589 bv_owner->buffer()->paragraph = p;
2595 LyXText::text_status LyXText::status() const
2601 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2603 // well as much as I know && binds more then || so the above and the
2604 // below are identical (this for your known use of parentesis!)
2605 // Now some explanation:
2606 // We should only go up with refreshing code so this means that if
2607 // we have a MORE refresh we should never set it to LITTLE if we still
2608 // didn't handle it (and then it will be UNCHANGED. Now as long as
2609 // we stay inside one LyXText this may work but we need to tell the
2610 // outermost LyXText that it should REALLY draw us if there is some
2611 // change in a Inset::LyXText. So you see that when we are inside a
2612 // inset's LyXText we give the LITTLE to the outermost LyXText to
2613 // tell'em that it should redraw the actual row (where the inset
2614 // resides! Capito?!
2616 if ((status_ != NEED_MORE_REFRESH)
2617 || (status_ == NEED_MORE_REFRESH
2618 && st != NEED_VERY_LITTLE_REFRESH))
2621 if (inset_owner && st != UNCHANGED) {
2622 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2623 if (!bview->text->refresh_row) {
2624 bview->text->refresh_row = bview->text->cursor.row();
2625 bview->text->refresh_y = bview->text->cursor.y() -
2626 bview->text->cursor.row()->baseline();