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"
25 #include "lyx_gui_misc.h"
27 #include "BufferView.h"
29 #include "CutAndPaste.h"
35 #include "FloatList.h"
37 #include "ParagraphParameters.h"
39 #include "insets/inseterror.h"
40 #include "insets/insetbib.h"
41 #include "insets/insetspecialchar.h"
42 #include "insets/insettext.h"
43 #include "insets/insetfloat.h"
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
57 LyXText::LyXText(BufferView * bv)
58 : number_of_rows(0), height(0), width(0), first(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),
66 LyXText::LyXText(InsetText * inset)
67 : number_of_rows(0), height(0), width(0), first(0),
68 bv_owner(0), inset_owner(inset), the_locking_inset(0),
69 need_break_row(0), refresh_y(0), refresh_row(0),
70 status_(LyXText::UNCHANGED), firstrow(0), lastrow(0),
74 void LyXText::init(BufferView * bview, bool reinit)
77 // Delete all rows, this does not touch the paragraphs!
78 Row * tmprow = firstrow;
80 tmprow = firstrow->next();
84 lastrow = refresh_row = need_break_row = 0;
85 width = height = copylayouttype = 0;
86 number_of_rows = first = refresh_y = 0;
87 status_ = LyXText::UNCHANGED;
91 Paragraph * par = ownerParagraph();
92 current_font = getFont(bview->buffer(), par, 0);
94 insertParagraph(bview, par, lastrow);
97 setCursorIntern(bview, firstrow->par(), 0);
98 selection.cursor = cursor;
104 // Delete all rows, this does not touch the paragraphs!
105 Row * tmprow = firstrow;
107 tmprow = firstrow->next();
116 LyXFont const realizeFont(LyXFont const & font,
120 LyXFont tmpfont(font);
121 Paragraph::depth_type par_depth = par->getDepth();
123 // Resolve against environment font information
124 while (par && par_depth && !tmpfont.resolved()) {
125 par = par->outerHook();
127 #ifndef INHERIT_LANGUAGE
128 tmpfont.realize(textclasslist.
129 Style(buf->params.textclass,
130 par->getLayout()).font);
132 tmpfont.realize(textclasslist.
133 Style(buf->params.textclass,
134 par->getLayout()).font,
135 buf->params.language);
137 par_depth = par->getDepth();
141 #ifndef INHERIT_LANGUAGE
142 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
144 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
145 buf->params.language);
154 // Gets the fully instantiated font at a given position in a paragraph
155 // Basically the same routine as Paragraph::getFont() in paragraph.C.
156 // The difference is that this one is used for displaying, and thus we
157 // are allowed to make cosmetic improvements. For instance make footnotes
159 // If position is -1, we get the layout font of the paragraph.
160 // If position is -2, we get the font of the manual label of the paragraph.
161 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
164 lyx::Assert(pos >= 0);
166 LyXLayout const & layout =
167 textclasslist.Style(buf->params.textclass, par->getLayout());
169 Paragraph::depth_type par_depth = par->getDepth();
170 // We specialize the 95% common case:
172 if (layout.labeltype == LABEL_MANUAL
173 && pos < beginningOfMainBody(buf, par)) {
175 LyXFont f = par->getFontSettings(buf->params,
177 #ifndef INHERIT_LANGUAGE
178 return f.realize(layout.reslabelfont);
180 return f.realize(layout.reslabelfont, buf->params.language);
183 LyXFont f = par->getFontSettings(buf->params, pos);
184 #ifndef INHERIT_LANGUAGE
185 return f.realize(layout.resfont);
187 return f.realize(layout.resfont, buf->params.language);
192 // The uncommon case need not be optimized as much
196 if (pos < beginningOfMainBody(buf, par)) {
198 layoutfont = layout.labelfont;
201 layoutfont = layout.font;
204 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
205 #ifndef INHERIT_LANGUAGE
206 tmpfont.realize(layoutfont);
208 tmpfont.realize(layoutfont, buf->params.language);
211 return realizeFont(tmpfont, buf, par);
215 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
217 LyXLayout const & layout =
218 textclasslist.Style(buf->params.textclass, par->getLayout());
220 Paragraph::depth_type par_depth = par->getDepth();
223 return layout.resfont;
226 return realizeFont(layout.font, buf, par);
230 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
232 LyXLayout const & layout =
233 textclasslist.Style(buf->params.textclass, par->getLayout());
235 Paragraph::depth_type par_depth = par->getDepth();
238 return layout.reslabelfont;
241 return realizeFont(layout.labelfont, buf, par);
245 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
246 pos_type pos, LyXFont const & fnt,
249 Buffer const * buf = bv->buffer();
250 LyXFont font = getFont(buf, par, pos);
251 font.update(fnt, buf->params.language, toggleall);
252 // Let the insets convert their font
253 if (par->isInset(pos)) {
254 Inset * inset = par->getInset(pos);
255 if (isEditableInset(inset)) {
256 UpdatableInset * uinset =
257 static_cast<UpdatableInset *>(inset);
258 uinset->setFont(bv, fnt, toggleall, true);
262 LyXLayout const & layout =
263 textclasslist.Style(buf->params.textclass,
266 // Get concrete layout font to reduce against
269 if (pos < beginningOfMainBody(buf, par))
270 layoutfont = layout.labelfont;
272 layoutfont = layout.font;
274 // Realize against environment font information
275 if (par->getDepth()){
276 Paragraph * tp = par;
277 while (!layoutfont.resolved() && tp && tp->getDepth()) {
278 tp = tp->outerHook();
280 #ifndef INHERIT_LANGUAGE
281 layoutfont.realize(textclasslist.
282 Style(buf->params.textclass,
283 tp->getLayout()).font);
285 layoutfont.realize(textclasslist.
286 Style(buf->params.textclass,
287 tp->getLayout()).font,
288 buf->params.language);
293 #ifndef INHERIT_LANGUAGE
294 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
296 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
297 buf->params.language);
300 // Now, reduce font against full layout font
301 font.reduce(layoutfont);
303 par->setFont(pos, font);
307 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
308 pos_type pos, LyXFont const & fnt)
312 LyXLayout const & layout =
313 textclasslist.Style(buf->params.textclass,
316 // Get concrete layout font to reduce against
319 if (pos < beginningOfMainBody(buf, par))
320 layoutfont = layout.labelfont;
322 layoutfont = layout.font;
324 // Realize against environment font information
325 if (par->getDepth()){
326 Paragraph * tp = par;
327 while (!layoutfont.resolved() && tp && tp->getDepth()) {
328 tp = tp->outerHook();
330 #ifndef INHERIT_LANGUAGE
331 layoutfont.realize(textclasslist.
332 Style(buf->params.textclass,
333 tp->getLayout()).font);
335 layoutfont.realize(textclasslist.
336 Style(buf->params.textclass,
337 tp->getLayout()).font,
338 buf->params.language);
343 #ifndef INHERIT_LANGUAGE
344 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
346 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont(),
347 buf->params.language);
350 // Now, reduce font against full layout font
351 font.reduce(layoutfont);
353 par->setFont(pos, font);
357 // inserts a new row behind the specified row, increments
358 // the touched counters
359 void LyXText::insertRow(Row * row, Paragraph * par,
362 Row * tmprow = new Row;
365 tmprow->next(firstrow);
368 tmprow->previous(row);
369 tmprow->next(row->next());
374 tmprow->next()->previous(tmprow);
376 if (tmprow->previous())
377 tmprow->previous()->next(tmprow);
389 // removes the row and reset the touched counters
390 void LyXText::removeRow(Row * row) const
392 Row * row_prev = row->previous();
394 row->next()->previous(row_prev);
396 firstrow = row->next();
397 // lyx::Assert(firstrow);
399 row_prev->next(row->next());
401 if (row == lastrow) {
402 lyx::Assert(!row->next());
405 if (refresh_row == row) {
406 refresh_row = row_prev ? row_prev : row->next();
409 height -= row->height(); // the text becomes smaller
412 --number_of_rows; // one row less
416 // remove all following rows of the paragraph of the specified row.
417 void LyXText::removeParagraph(Row * row) const
419 Paragraph * tmppar = row->par();
423 while (row && row->par() == tmppar) {
424 tmprow = row->next();
431 // insert the specified paragraph behind the specified row
432 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
435 insertRow(row, par, 0); /* insert a new row, starting
438 setCounter(bview->buffer(), par); // set the counters
440 // and now append the whole paragraph behind the new row
443 appendParagraph(bview, firstrow);
445 row->next()->height(0);
446 appendParagraph(bview, row->next());
451 Inset * LyXText::getInset() const
454 if (cursor.pos() == 0 && cursor.par()->bibkey) {
455 inset = cursor.par()->bibkey;
456 } else if (cursor.pos() < cursor.par()->size()
457 && cursor.par()->isInset(cursor.pos())) {
458 inset = cursor.par()->getInset(cursor.pos());
464 void LyXText::toggleInset(BufferView * bview)
466 Inset * inset = getInset();
467 if (!isEditableInset(inset))
469 //bview->owner()->message(inset->editMessage());
471 // do we want to keep this?? (JMarc)
472 if (!isHighlyEditableInset(inset))
473 setCursorParUndo(bview);
475 if (inset->isOpen()) {
481 inset->open(bview, !inset->isOpen());
486 /* used in setlayout */
487 // Asger is not sure we want to do this...
488 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
491 LyXLayout const & layout =
492 textclasslist.Style(buf->params.textclass, par->getLayout());
495 for (pos_type pos = 0; pos < par->size(); ++pos) {
496 if (pos < beginningOfMainBody(buf, par))
497 layoutfont = layout.labelfont;
499 layoutfont = layout.font;
501 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
502 tmpfont.reduce(layoutfont);
503 par->setFont(pos, tmpfont);
508 Paragraph * LyXText::setLayout(BufferView * bview,
509 LyXCursor & cur, LyXCursor & sstart_cur,
510 LyXCursor & send_cur,
511 lyx::layout_type layout)
513 Paragraph * endpar = send_cur.par()->next();
514 Paragraph * undoendpar = endpar;
516 if (endpar && endpar->getDepth()) {
517 while (endpar && endpar->getDepth()) {
518 endpar = endpar->next();
522 endpar = endpar->next(); // because of parindents etc.
525 setUndo(bview, Undo::EDIT,
526 sstart_cur.par(), undoendpar);
528 // ok we have a selection. This is always between sstart_cur
529 // and sel_end cursor
532 LyXLayout const & lyxlayout =
533 textclasslist.Style(bview->buffer()->params.textclass, layout);
535 while (cur.par() != send_cur.par()) {
536 cur.par()->setLayout(layout);
537 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
538 Paragraph * fppar = cur.par();
539 fppar->params().spaceTop(lyxlayout.fill_top ?
540 VSpace(VSpace::VFILL)
541 : VSpace(VSpace::NONE));
542 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
543 VSpace(VSpace::VFILL)
544 : VSpace(VSpace::NONE));
545 if (lyxlayout.margintype == MARGIN_MANUAL)
546 cur.par()->setLabelWidthString(lyxlayout.labelstring());
547 if (lyxlayout.labeltype != LABEL_BIBLIO
549 delete fppar->bibkey;
552 cur.par(cur.par()->next());
554 cur.par()->setLayout(layout);
555 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
556 Paragraph * fppar = cur.par();
557 fppar->params().spaceTop(lyxlayout.fill_top ?
558 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
559 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
560 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
561 if (lyxlayout.margintype == MARGIN_MANUAL)
562 cur.par()->setLabelWidthString(lyxlayout.labelstring());
563 if (lyxlayout.labeltype != LABEL_BIBLIO
565 delete fppar->bibkey;
572 // set layout over selection and make a total rebreak of those paragraphs
573 void LyXText::setLayout(BufferView * bview, lyx::layout_type layout)
575 LyXCursor tmpcursor = cursor; /* store the current cursor */
577 // if there is no selection just set the layout
578 // of the current paragraph */
579 if (!selection.set()) {
580 selection.start = cursor; // dummy selection
581 selection.end = cursor;
583 Paragraph * endpar = setLayout(bview, cursor, selection.start,
584 selection.end, layout);
585 redoParagraphs(bview, selection.start, endpar);
587 // we have to reset the selection, because the
588 // geometry could have changed
589 setCursor(bview, selection.start.par(),
590 selection.start.pos(), false);
591 selection.cursor = cursor;
592 setCursor(bview, selection.end.par(), selection.end.pos(), false);
593 updateCounters(bview, cursor.row());
596 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
600 // increment depth over selection and
601 // make a total rebreak of those paragraphs
602 void LyXText::incDepth(BufferView * bview)
604 // If there is no selection, just use the current paragraph
605 if (!selection.set()) {
606 selection.start = cursor; // dummy selection
607 selection.end = cursor;
610 // We end at the next paragraph with depth 0
611 Paragraph * endpar = selection.end.par()->next();
613 Paragraph * undoendpar = endpar;
615 if (endpar && endpar->getDepth()) {
616 while (endpar && endpar->getDepth()) {
617 endpar = endpar->next();
621 endpar = endpar->next(); // because of parindents etc.
624 setUndo(bview, Undo::EDIT,
625 selection.start.par(), undoendpar);
627 LyXCursor tmpcursor = cursor; // store the current cursor
629 // ok we have a selection. This is always between sel_start_cursor
630 // and sel_end cursor
631 cursor = selection.start;
633 bool anything_changed = false;
636 // NOTE: you can't change the depth of a bibliography entry
638 textclasslist.Style(bview->buffer()->params.textclass,
639 cursor.par()->getLayout()
640 ).labeltype != LABEL_BIBLIO) {
641 Paragraph * prev = cursor.par()->previous();
644 && (prev->getDepth() - cursor.par()->getDepth() > 0
645 || (prev->getDepth() == cursor.par()->getDepth()
646 && textclasslist.Style(bview->buffer()->params.textclass,
647 prev->getLayout()).isEnvironment()))) {
648 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
649 anything_changed = true;
652 if (cursor.par() == selection.end.par())
654 cursor.par(cursor.par()->next());
657 // if nothing changed set all depth to 0
658 if (!anything_changed) {
659 cursor = selection.start;
660 while (cursor.par() != selection.end.par()) {
661 cursor.par()->params().depth(0);
662 cursor.par(cursor.par()->next());
664 cursor.par()->params().depth(0);
667 redoParagraphs(bview, selection.start, endpar);
669 // we have to reset the selection, because the
670 // geometry could have changed
671 setCursor(bview, selection.start.par(), selection.start.pos());
672 selection.cursor = cursor;
673 setCursor(bview, selection.end.par(), selection.end.pos());
674 updateCounters(bview, cursor.row());
677 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
681 // decrement depth over selection and
682 // make a total rebreak of those paragraphs
683 void LyXText::decDepth(BufferView * bview)
685 // if there is no selection just set the layout
686 // of the current paragraph
687 if (!selection.set()) {
688 selection.start = cursor; // dummy selection
689 selection.end = cursor;
691 Paragraph * endpar = selection.end.par()->next();
692 Paragraph * undoendpar = endpar;
694 if (endpar && endpar->getDepth()) {
695 while (endpar && endpar->getDepth()) {
696 endpar = endpar->next();
700 endpar = endpar->next(); // because of parindents etc.
703 setUndo(bview, Undo::EDIT,
704 selection.start.par(), undoendpar);
706 LyXCursor tmpcursor = cursor; // store the current cursor
708 // ok we have a selection. This is always between sel_start_cursor
709 // and sel_end cursor
710 cursor = selection.start;
713 if (cursor.par()->params().depth()) {
714 cursor.par()->params()
715 .depth(cursor.par()->params().depth() - 1);
717 if (cursor.par() == selection.end.par()) {
720 cursor.par(cursor.par()->next());
723 redoParagraphs(bview, selection.start, endpar);
725 // we have to reset the selection, because the
726 // geometry could have changed
727 setCursor(bview, selection.start.par(),
728 selection.start.pos());
729 selection.cursor = cursor;
730 setCursor(bview, selection.end.par(), selection.end.pos());
731 updateCounters(bview, cursor.row());
734 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
738 // set font over selection and make a total rebreak of those paragraphs
739 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
741 // if there is no selection just set the current_font
742 if (!selection.set()) {
743 // Determine basis font
745 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
747 layoutfont = getLabelFont(bview->buffer(),
750 layoutfont = getLayoutFont(bview->buffer(),
753 // Update current font
754 real_current_font.update(font,
755 bview->buffer()->params.language,
758 // Reduce to implicit settings
759 current_font = real_current_font;
760 current_font.reduce(layoutfont);
761 // And resolve it completely
762 #ifndef INHERIT_LANGUAGE
763 real_current_font.realize(layoutfont);
765 real_current_font.realize(layoutfont,
766 bview->buffer()->params.language);
771 LyXCursor tmpcursor = cursor; // store the current cursor
773 // ok we have a selection. This is always between sel_start_cursor
774 // and sel_end cursor
776 setUndo(bview, Undo::EDIT,
777 selection.start.par(), selection.end.par()->next());
779 cursor = selection.start;
780 while (cursor.par() != selection.end.par() ||
781 (cursor.pos() < selection.end.pos()))
783 if (cursor.pos() < cursor.par()->size()) {
784 // an open footnote should behave
786 setCharFont(bview, cursor.par(), cursor.pos(),
788 cursor.pos(cursor.pos() + 1);
791 cursor.par(cursor.par()->next());
796 redoParagraphs(bview, selection.start, selection.end.par()->next());
798 // we have to reset the selection, because the
799 // geometry could have changed, but we keep
800 // it for user convenience
801 setCursor(bview, selection.start.par(), selection.start.pos());
802 selection.cursor = cursor;
803 setCursor(bview, selection.end.par(), selection.end.pos());
805 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
806 tmpcursor.boundary());
810 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
812 Row * tmprow = cur.row();
813 int y = cur.y() - tmprow->baseline();
815 setHeightOfRow(bview, tmprow);
817 while (tmprow->previous()
818 && tmprow->previous()->par() == tmprow->par()) {
819 tmprow = tmprow->previous();
820 y -= tmprow->height();
821 setHeightOfRow(bview, tmprow);
824 // we can set the refreshing parameters now
825 status(bview, LyXText::NEED_MORE_REFRESH);
827 refresh_row = tmprow;
828 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
832 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
834 Row * tmprow = cur.row();
836 int y = cur.y() - tmprow->baseline();
837 setHeightOfRow(bview, tmprow);
839 while (tmprow->previous()
840 && tmprow->previous()->par() == tmprow->par()) {
841 tmprow = tmprow->previous();
842 y -= tmprow->height();
845 // we can set the refreshing parameters now
846 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
848 refresh_row = tmprow;
850 status(bview, LyXText::NEED_MORE_REFRESH);
851 setCursor(bview, cur.par(), cur.pos());
855 // deletes and inserts again all paragaphs between the cursor
856 // and the specified par
857 // This function is needed after SetLayout and SetFont etc.
858 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
859 Paragraph const * endpar) const
862 Paragraph * tmppar = 0;
863 Paragraph * first_phys_par = 0;
865 Row * tmprow = cur.row();
867 int y = cur.y() - tmprow->baseline();
869 if (!tmprow->previous()) {
870 // a trick/hack for UNDO
871 // This is needed because in an UNDO/REDO we could have changed
872 // the ownerParagrah() so the paragraph inside the row is NOT
873 // my really first par anymore. Got it Lars ;) (Jug 20011206)
874 first_phys_par = ownerParagraph();
876 first_phys_par = tmprow->par();
877 while (tmprow->previous()
878 && tmprow->previous()->par() == first_phys_par)
880 tmprow = tmprow->previous();
881 y -= tmprow->height();
885 // we can set the refreshing parameters now
886 status(bview, LyXText::NEED_MORE_REFRESH);
888 refresh_row = tmprow->previous(); /* the real refresh row will
889 be deleted, so I store
893 tmppar = tmprow->next()->par();
896 while (tmprow->next() && tmppar != endpar) {
897 removeRow(tmprow->next());
898 if (tmprow->next()) {
899 tmppar = tmprow->next()->par();
905 // remove the first one
906 tmprow2 = tmprow; /* this is because tmprow->previous()
908 tmprow = tmprow->previous();
911 tmppar = first_phys_par;
915 insertParagraph(bview, tmppar, tmprow);
919 while (tmprow->next()
920 && tmprow->next()->par() == tmppar) {
921 tmprow = tmprow->next();
923 tmppar = tmppar->next();
925 } while (tmppar && tmppar != endpar);
927 // this is because of layout changes
929 refresh_y -= refresh_row->height();
930 setHeightOfRow(bview, refresh_row);
932 refresh_row = firstrow;
934 setHeightOfRow(bview, refresh_row);
937 if (tmprow && tmprow->next())
938 setHeightOfRow(bview, tmprow->next());
942 bool LyXText::fullRebreak(BufferView * bview)
948 if (need_break_row) {
949 breakAgain(bview, need_break_row);
957 // important for the screen
960 /* the cursor set functions have a special mechanism. When they
961 * realize, that you left an empty paragraph, they will delete it.
962 * They also delete the corresponding row */
964 // need the selection cursor:
965 void LyXText::setSelection(BufferView * bview)
967 bool const lsel = selection.set();
969 if (!selection.set()) {
970 last_sel_cursor = selection.cursor;
971 selection.start = selection.cursor;
972 selection.end = selection.cursor;
977 // first the toggling area
978 if (cursor.y() < last_sel_cursor.y()
979 || (cursor.y() == last_sel_cursor.y()
980 && cursor.x() < last_sel_cursor.x())) {
981 toggle_end_cursor = last_sel_cursor;
982 toggle_cursor = cursor;
984 toggle_end_cursor = cursor;
985 toggle_cursor = last_sel_cursor;
988 last_sel_cursor = cursor;
990 // and now the whole selection
992 if (selection.cursor.par() == cursor.par())
993 if (selection.cursor.pos() < cursor.pos()) {
994 selection.end = cursor;
995 selection.start = selection.cursor;
997 selection.end = selection.cursor;
998 selection.start = cursor;
1000 else if (selection.cursor.y() < cursor.y() ||
1001 (selection.cursor.y() == cursor.y()
1002 && selection.cursor.x() < cursor.x())) {
1003 selection.end = cursor;
1004 selection.start = selection.cursor;
1007 selection.end = selection.cursor;
1008 selection.start = cursor;
1011 // a selection with no contents is not a selection
1012 if (selection.start.par() == selection.end.par() &&
1013 selection.start.pos() == selection.end.pos())
1014 selection.set(false);
1016 if (inset_owner && (selection.set() || lsel))
1017 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
1021 string const LyXText::selectionAsString(Buffer const * buffer,
1024 if (!selection.set()) return string();
1027 // Special handling if the whole selection is within one paragraph
1028 if (selection.start.par() == selection.end.par()) {
1029 result += selection.start.par()->asString(buffer,
1030 selection.start.pos(),
1031 selection.end.pos(),
1036 // The selection spans more than one paragraph
1038 // First paragraph in selection
1039 result += selection.start.par()->asString(buffer,
1040 selection.start.pos(),
1041 selection.start.par()->size(),
1045 // The paragraphs in between (if any)
1046 LyXCursor tmpcur(selection.start);
1047 tmpcur.par(tmpcur.par()->next());
1048 while (tmpcur.par() != selection.end.par()) {
1049 result += tmpcur.par()->asString(buffer, 0,
1050 tmpcur.par()->size(),
1052 tmpcur.par(tmpcur.par()->next());
1055 // Last paragraph in selection
1056 result += selection.end.par()->asString(buffer, 0,
1057 selection.end.pos(), label);
1063 void LyXText::clearSelection() const
1065 selection.set(false);
1066 selection.mark(false);
1067 last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1071 void LyXText::cursorHome(BufferView * bview) const
1073 setCursor(bview, cursor.par(), cursor.row()->pos());
1077 void LyXText::cursorEnd(BufferView * bview) const
1079 if (!cursor.row()->next()
1080 || cursor.row()->next()->par() != cursor.row()->par()) {
1081 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1083 if (cursor.par()->size() &&
1084 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1085 || cursor.par()->isNewline(rowLast(cursor.row())))) {
1086 setCursor(bview, cursor.par(), rowLast(cursor.row()));
1088 setCursor(bview,cursor.par(),
1089 rowLast(cursor.row()) + 1);
1095 void LyXText::cursorTop(BufferView * bview) const
1097 while (cursor.par()->previous())
1098 cursor.par(cursor.par()->previous());
1099 setCursor(bview, cursor.par(), 0);
1103 void LyXText::cursorBottom(BufferView * bview) const
1105 while (cursor.par()->next())
1106 cursor.par(cursor.par()->next());
1107 setCursor(bview, cursor.par(), cursor.par()->size());
1111 void LyXText::toggleFree(BufferView * bview,
1112 LyXFont const & font, bool toggleall)
1114 // If the mask is completely neutral, tell user
1115 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1116 // Could only happen with user style
1117 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1121 // Try implicit word selection
1122 // If there is a change in the language the implicit word selection
1124 LyXCursor resetCursor = cursor;
1125 bool implicitSelection = (font.language() == ignore_language
1126 && font.number() == LyXFont::IGNORE)
1127 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1130 setFont(bview, font, toggleall);
1132 // Implicit selections are cleared afterwards
1133 //and cursor is set to the original position.
1134 if (implicitSelection) {
1136 cursor = resetCursor;
1137 setCursor(bview, cursor.par(), cursor.pos());
1138 selection.cursor = cursor;
1141 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1145 string LyXText::getStringToIndex(BufferView * bview)
1149 // Try implicit word selection
1150 // If there is a change in the language the implicit word selection
1152 LyXCursor resetCursor = cursor;
1153 bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1155 if (!selection.set()) {
1156 bview->owner()->message(_("Nothing to index!"));
1159 if (selection.start.par() != selection.end.par()) {
1160 bview->owner()->message(_("Cannot index more than one paragraph!"));
1164 idxstring = selectionAsString(bview->buffer(), false);
1166 // Implicit selections are cleared afterwards
1167 //and cursor is set to the original position.
1168 if (implicitSelection) {
1170 cursor = resetCursor;
1171 setCursor(bview, cursor.par(), cursor.pos());
1172 selection.cursor = cursor;
1178 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1179 Paragraph const * par) const
1181 if (textclasslist.Style(buf->params.textclass,
1182 par->getLayout()).labeltype != LABEL_MANUAL)
1185 return par->beginningOfMainBody();
1189 /* the DTP switches for paragraphs. LyX will store them in the
1190 * first physicla paragraph. When a paragraph is broken, the top settings
1191 * rest, the bottom settings are given to the new one. So I can make shure,
1192 * they do not duplicate themself and you cannnot make dirty things with
1195 void LyXText::setParagraph(BufferView * bview,
1196 bool line_top, bool line_bottom,
1197 bool pagebreak_top, bool pagebreak_bottom,
1198 VSpace const & space_top,
1199 VSpace const & space_bottom,
1200 Spacing const & spacing,
1202 string labelwidthstring,
1205 LyXCursor tmpcursor = cursor;
1206 if (!selection.set()) {
1207 selection.start = cursor;
1208 selection.end = cursor;
1211 // make sure that the depth behind the selection are restored, too
1212 Paragraph * endpar = selection.end.par()->next();
1213 Paragraph * undoendpar = endpar;
1215 if (endpar && endpar->getDepth()) {
1216 while (endpar && endpar->getDepth()) {
1217 endpar = endpar->next();
1218 undoendpar = endpar;
1222 // because of parindents etc.
1223 endpar = endpar->next();
1226 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1229 Paragraph * tmppar = selection.end.par();
1230 while (tmppar != selection.start.par()->previous()) {
1231 setCursor(bview, tmppar, 0);
1232 status(bview, LyXText::NEED_MORE_REFRESH);
1233 refresh_row = cursor.row();
1234 refresh_y = cursor.y() - cursor.row()->baseline();
1235 cursor.par()->params().lineTop(line_top);
1236 cursor.par()->params().lineBottom(line_bottom);
1237 cursor.par()->params().pagebreakTop(pagebreak_top);
1238 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1239 cursor.par()->params().spaceTop(space_top);
1240 cursor.par()->params().spaceBottom(space_bottom);
1241 cursor.par()->params().spacing(spacing);
1242 // does the layout allow the new alignment?
1243 if (align == LYX_ALIGN_LAYOUT)
1244 align = textclasslist
1245 .Style(bview->buffer()->params.textclass,
1246 cursor.par()->getLayout()).align;
1247 if (align & textclasslist
1248 .Style(bview->buffer()->params.textclass,
1249 cursor.par()->getLayout()).alignpossible) {
1250 if (align == textclasslist
1251 .Style(bview->buffer()->params.textclass,
1252 cursor.par()->getLayout()).align)
1253 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1255 cursor.par()->params().align(align);
1257 cursor.par()->setLabelWidthString(labelwidthstring);
1258 cursor.par()->params().noindent(noindent);
1259 tmppar = cursor.par()->previous();
1262 redoParagraphs(bview, selection.start, endpar);
1265 setCursor(bview, selection.start.par(), selection.start.pos());
1266 selection.cursor = cursor;
1267 setCursor(bview, selection.end.par(), selection.end.pos());
1268 setSelection(bview);
1269 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1271 bview->updateInset(inset_owner, true);
1275 char loweralphaCounter(int n)
1277 if (n < 1 || n > 26)
1287 char alphaCounter(int n)
1289 if (n < 1 || n > 26)
1297 char hebrewCounter(int n)
1299 static const char hebrew[22] = {
1300 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1301 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1302 '÷', 'ø', 'ù', 'ú'
1304 if (n < 1 || n > 22)
1312 string const romanCounter(int n)
1314 static char const * roman[20] = {
1315 "i", "ii", "iii", "iv", "v",
1316 "vi", "vii", "viii", "ix", "x",
1317 "xi", "xii", "xiii", "xiv", "xv",
1318 "xvi", "xvii", "xviii", "xix", "xx"
1320 if (n < 1 || n > 20)
1329 // set the counter of a paragraph. This includes the labels
1330 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1332 LyXLayout const & layout =
1333 textclasslist.Style(buf->params.textclass,
1336 LyXTextClass const & textclass =
1337 textclasslist.TextClass(buf->params.textclass);
1339 // copy the prev-counters to this one,
1340 // unless this is the first paragraph
1341 if (par->previous()) {
1342 for (int i = 0; i < 10; ++i) {
1343 par->setCounter(i, par->previous()->getFirstCounter(i));
1345 par->params().appendix(par->previous()->params().appendix());
1346 if (!par->params().appendix() && par->params().startOfAppendix()) {
1347 par->params().appendix(true);
1348 for (int i = 0; i < 10; ++i) {
1349 par->setCounter(i, 0);
1352 par->enumdepth = par->previous()->enumdepth;
1353 par->itemdepth = par->previous()->itemdepth;
1355 for (int i = 0; i < 10; ++i) {
1356 par->setCounter(i, 0);
1358 par->params().appendix(par->params().startOfAppendix());
1363 /* Maybe we have to increment the enumeration depth.
1364 * BUT, enumeration in a footnote is considered in isolation from its
1365 * surrounding paragraph so don't increment if this is the
1366 * first line of the footnote
1367 * AND, bibliographies can't have their depth changed ie. they
1368 * are always of depth 0
1371 && par->previous()->getDepth() < par->getDepth()
1372 && textclasslist.Style(buf->params.textclass,
1373 par->previous()->getLayout()
1374 ).labeltype == LABEL_COUNTER_ENUMI
1375 && par->enumdepth < 3
1376 && layout.labeltype != LABEL_BIBLIO) {
1380 // Maybe we have to decrement the enumeration depth, see note above
1382 && par->previous()->getDepth() > par->getDepth()
1383 && layout.labeltype != LABEL_BIBLIO) {
1384 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1385 par->setCounter(6 + par->enumdepth,
1386 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1387 /* reset the counters.
1388 * A depth change is like a breaking layout
1390 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1391 par->setCounter(i, 0);
1394 if (!par->params().labelString().empty()) {
1395 par->params().labelString(string());
1398 if (layout.margintype == MARGIN_MANUAL) {
1399 if (par->params().labelWidthString().empty()) {
1400 par->setLabelWidthString(layout.labelstring());
1403 par->setLabelWidthString(string());
1406 // is it a layout that has an automatic label?
1407 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1409 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1410 if (i >= 0 && i<= buf->params.secnumdepth) {
1411 par->incCounter(i); // increment the counter
1413 // Is there a label? Useful for Chapter layout
1414 if (!par->params().appendix()) {
1415 if (!layout.labelstring().empty())
1416 par->params().labelString(layout.labelstring());
1418 par->params().labelString(string());
1420 if (!layout.labelstring_appendix().empty())
1421 par->params().labelString(layout.labelstring_appendix());
1423 par->params().labelString(string());
1428 if (!par->params().appendix()) {
1429 switch (2 * LABEL_COUNTER_CHAPTER -
1430 textclass.maxcounter() + i) {
1431 case LABEL_COUNTER_CHAPTER:
1432 s << par->getCounter(i);
1434 case LABEL_COUNTER_SECTION:
1435 s << par->getCounter(i - 1) << '.'
1436 << par->getCounter(i);
1438 case LABEL_COUNTER_SUBSECTION:
1439 s << par->getCounter(i - 2) << '.'
1440 << par->getCounter(i - 1) << '.'
1441 << par->getCounter(i);
1443 case LABEL_COUNTER_SUBSUBSECTION:
1444 s << par->getCounter(i - 3) << '.'
1445 << par->getCounter(i - 2) << '.'
1446 << par->getCounter(i - 1) << '.'
1447 << par->getCounter(i);
1450 case LABEL_COUNTER_PARAGRAPH:
1451 s << par->getCounter(i - 4) << '.'
1452 << par->getCounter(i - 3) << '.'
1453 << par->getCounter(i - 2) << '.'
1454 << par->getCounter(i - 1) << '.'
1455 << par->getCounter(i);
1457 case LABEL_COUNTER_SUBPARAGRAPH:
1458 s << par->getCounter(i - 5) << '.'
1459 << par->getCounter(i - 4) << '.'
1460 << par->getCounter(i - 3) << '.'
1461 << par->getCounter(i - 2) << '.'
1462 << par->getCounter(i - 1) << '.'
1463 << par->getCounter(i);
1467 // Can this ever be reached? And in the
1468 // case it is, how can this be correct?
1470 s << par->getCounter(i) << '.';
1473 } else { // appendix
1474 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1475 case LABEL_COUNTER_CHAPTER:
1476 if (par->isRightToLeftPar(buf->params))
1477 s << hebrewCounter(par->getCounter(i));
1479 s << alphaCounter(par->getCounter(i));
1481 case LABEL_COUNTER_SECTION:
1482 if (par->isRightToLeftPar(buf->params))
1483 s << hebrewCounter(par->getCounter(i - 1));
1485 s << alphaCounter(par->getCounter(i - 1));
1488 << par->getCounter(i);
1491 case LABEL_COUNTER_SUBSECTION:
1492 if (par->isRightToLeftPar(buf->params))
1493 s << hebrewCounter(par->getCounter(i - 2));
1495 s << alphaCounter(par->getCounter(i - 2));
1498 << par->getCounter(i-1) << '.'
1499 << par->getCounter(i);
1502 case LABEL_COUNTER_SUBSUBSECTION:
1503 if (par->isRightToLeftPar(buf->params))
1504 s << hebrewCounter(par->getCounter(i-3));
1506 s << alphaCounter(par->getCounter(i-3));
1509 << par->getCounter(i-2) << '.'
1510 << par->getCounter(i-1) << '.'
1511 << par->getCounter(i);
1514 case LABEL_COUNTER_PARAGRAPH:
1515 if (par->isRightToLeftPar(buf->params))
1516 s << hebrewCounter(par->getCounter(i-4));
1518 s << alphaCounter(par->getCounter(i-4));
1521 << par->getCounter(i-3) << '.'
1522 << par->getCounter(i-2) << '.'
1523 << par->getCounter(i-1) << '.'
1524 << par->getCounter(i);
1527 case LABEL_COUNTER_SUBPARAGRAPH:
1528 if (par->isRightToLeftPar(buf->params))
1529 s << hebrewCounter(par->getCounter(i-5));
1531 s << alphaCounter(par->getCounter(i-5));
1534 << par->getCounter(i-4) << '.'
1535 << par->getCounter(i-3) << '.'
1536 << par->getCounter(i-2) << '.'
1537 << par->getCounter(i-1) << '.'
1538 << par->getCounter(i);
1542 // Can this ever be reached? And in the
1543 // case it is, how can this be correct?
1545 s << par->getCounter(i) << '.';
1551 par->params().labelString(par->params().labelString() +s.str().c_str());
1552 // We really want to remove the c_str as soon as
1555 for (i++; i < 10; ++i) {
1556 // reset the following counters
1557 par->setCounter(i, 0);
1559 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1560 for (i++; i < 10; ++i) {
1561 // reset the following counters
1562 par->setCounter(i, 0);
1564 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1565 par->incCounter(i + par->enumdepth);
1566 int number = par->getCounter(i + par->enumdepth);
1570 switch (par->enumdepth) {
1572 if (par->isRightToLeftPar(buf->params))
1574 << hebrewCounter(number)
1578 << loweralphaCounter(number)
1582 if (par->isRightToLeftPar(buf->params))
1583 s << '.' << romanCounter(number);
1585 s << romanCounter(number) << '.';
1588 if (par->isRightToLeftPar(buf->params))
1590 << alphaCounter(number);
1592 s << alphaCounter(number)
1596 if (par->isRightToLeftPar(buf->params))
1603 par->params().labelString(s.str().c_str());
1605 for (i += par->enumdepth + 1; i < 10; ++i) {
1606 // reset the following counters
1607 par->setCounter(i, 0);
1611 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1612 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1614 int number = par->getCounter(i);
1616 InsetCommandParams p( "bibitem" );
1617 par->bibkey = new InsetBibKey(p);
1619 par->bibkey->setCounter(number);
1620 par->params().labelString(layout.labelstring());
1622 // In biblio should't be following counters but...
1624 string s = layout.labelstring();
1626 // the caption hack:
1627 if (layout.labeltype == LABEL_SENSITIVE) {
1628 bool isOK (par->inInset() && par->inInset()->owner() &&
1629 (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1632 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1634 = floatList.getType(tmp->type());
1635 // We should get the correct number here too.
1636 s = fl.name() + " #:";
1638 /* par->SetLayout(0);
1639 s = layout->labelstring; */
1640 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1641 ? " :úåòîùî øñç" : "Senseless: ";
1644 par->params().labelString(s);
1646 /* reset the enumeration counter. They are always resetted
1647 * when there is any other layout between */
1648 for (int i = 6 + par->enumdepth; i < 10; ++i)
1649 par->setCounter(i, 0);
1654 // Updates all counters BEHIND the row. Changed paragraphs
1655 // with a dynamic left margin will be rebroken.
1656 void LyXText::updateCounters(BufferView * bview, Row * row) const
1664 par = row->par()->next();
1668 while (row->par() != par)
1671 setCounter(bview->buffer(), par);
1673 // now check for the headline layouts. remember that they
1674 // have a dynamic left margin
1675 if ((textclasslist.Style(bview->buffer()->params.textclass,
1676 par->layout).margintype == MARGIN_DYNAMIC
1677 || textclasslist.Style(bview->buffer()->params.textclass,
1678 par->layout).labeltype == LABEL_SENSITIVE)) {
1680 // Rebreak the paragraph
1681 removeParagraph(row);
1682 appendParagraph(bview, row);
1689 void LyXText::insertInset(BufferView * bview, Inset * inset)
1691 if (!cursor.par()->insetAllowed(inset->lyxCode()))
1693 // I don't know if this is necessary here (Jug 20020102)
1694 setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1695 cursor.par()->insertInset(cursor.pos(), inset);
1696 // Just to rebreak and refresh correctly.
1697 // The character will not be inserted a second time
1698 insertChar(bview, Paragraph::META_INSET);
1700 // If we enter a highly editable inset the cursor should be to before
1701 // the inset. This couldn't happen before as Undo was not handled inside
1702 // inset now after the Undo LyX tries to call inset->Edit(...) again
1703 // and cannot do this as the cursor is behind the inset and GetInset
1704 // does not return the inset!
1705 if (isHighlyEditableInset(inset)) {
1706 cursorLeft(bview, true);
1712 void LyXText::copyEnvironmentType()
1714 copylayouttype = cursor.par()->getLayout();
1718 void LyXText::pasteEnvironmentType(BufferView * bview)
1720 setLayout(bview, copylayouttype);
1724 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1726 // Stuff what we got on the clipboard. Even if there is no selection.
1728 // There is a problem with having the stuffing here in that the
1729 // larger the selection the slower LyX will get. This can be
1730 // solved by running the line below only when the selection has
1731 // finished. The solution used currently just works, to make it
1732 // faster we need to be more clever and probably also have more
1733 // calls to stuffClipboard. (Lgb)
1734 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1736 // This doesn't make sense, if there is no selection
1737 if (!selection.set())
1740 // OK, we have a selection. This is always between selection.start
1741 // and selection.end
1743 // make sure that the depth behind the selection are restored, too
1744 Paragraph * endpar = selection.end.par()->next();
1745 Paragraph * undoendpar = endpar;
1747 if (endpar && endpar->getDepth()) {
1748 while (endpar && endpar->getDepth()) {
1749 endpar = endpar->next();
1750 undoendpar = endpar;
1752 } else if (endpar) {
1753 endpar = endpar->next(); // because of parindents etc.
1756 setUndo(bview, Undo::DELETE,
1757 selection.start.par(), undoendpar);
1759 // there are two cases: cut only within one paragraph or
1760 // more than one paragraph
1761 if (selection.start.par() == selection.end.par()) {
1762 // only within one paragraph
1763 endpar = selection.end.par();
1764 int pos = selection.end.pos();
1765 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1766 selection.start.pos(), pos,
1767 bview->buffer()->params.textclass,
1769 selection.end.pos(pos);
1771 endpar = selection.end.par();
1772 int pos = selection.end.pos();
1773 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1774 selection.start.pos(), pos,
1775 bview->buffer()->params.textclass,
1778 selection.end.par(endpar);
1779 selection.end.pos(pos);
1780 cursor.pos(selection.end.pos());
1782 endpar = endpar->next();
1784 // sometimes necessary
1786 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1788 redoParagraphs(bview, selection.start, endpar);
1790 // cutSelection can invalidate the cursor so we need to set
1792 cursor = selection.start;
1794 // need a valid cursor. (Lgb)
1797 setCursor(bview, cursor.par(), cursor.pos());
1798 selection.cursor = cursor;
1799 updateCounters(bview, cursor.row());
1803 void LyXText::copySelection(BufferView * bview)
1805 // stuff the selection onto the X clipboard, from an explicit copy request
1806 bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1808 // this doesnt make sense, if there is no selection
1809 if (!selection.set())
1812 // ok we have a selection. This is always between selection.start
1813 // and sel_end cursor
1815 // copy behind a space if there is one
1816 while (selection.start.par()->size() > selection.start.pos()
1817 && selection.start.par()->isLineSeparator(selection.start.pos())
1818 && (selection.start.par() != selection.end.par()
1819 || selection.start.pos() < selection.end.pos()))
1820 selection.start.pos(selection.start.pos() + 1);
1822 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1823 selection.start.pos(), selection.end.pos(),
1824 bview->buffer()->params.textclass);
1828 void LyXText::pasteSelection(BufferView * bview)
1830 // this does not make sense, if there is nothing to paste
1831 if (!CutAndPaste::checkPastePossible(cursor.par()))
1834 setUndo(bview, Undo::INSERT,
1835 cursor.par(), cursor.par()->next());
1838 Paragraph * actpar = cursor.par();
1839 int pos = cursor.pos();
1841 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1842 bview->buffer()->params.textclass);
1844 redoParagraphs(bview, cursor, endpar);
1846 setCursor(bview, cursor.par(), cursor.pos());
1849 setCursor(bview, actpar, pos);
1850 updateCounters(bview, cursor.row());
1854 // sets the selection over the number of characters of string, no check!!
1855 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1860 selection.cursor = cursor;
1861 for (string::size_type i = 0; i < str.length(); ++i)
1863 setSelection(bview);
1867 // simple replacing. The font of the first selected character is used
1868 void LyXText::replaceSelectionWithString(BufferView * bview,
1871 setCursorParUndo(bview);
1874 if (!selection.set()) { // create a dummy selection
1875 selection.end = cursor;
1876 selection.start = cursor;
1879 // Get font setting before we cut
1880 pos_type pos = selection.end.pos();
1881 LyXFont const font = selection.start.par()
1882 ->getFontSettings(bview->buffer()->params,
1883 selection.start.pos());
1885 // Insert the new string
1886 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1887 selection.end.par()->insertChar(pos, (*cit), font);
1891 // Cut the selection
1892 cutSelection(bview, true, false);
1898 // needed to insert the selection
1899 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1901 Paragraph * par = cursor.par();
1902 pos_type pos = cursor.pos();
1903 Paragraph * endpar = cursor.par()->next();
1905 setCursorParUndo(bview);
1907 // only to be sure, should not be neccessary
1910 bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1912 redoParagraphs(bview, cursor, endpar);
1913 setCursor(bview, cursor.par(), cursor.pos());
1914 selection.cursor = cursor;
1915 setCursor(bview, par, pos);
1916 setSelection(bview);
1920 // turns double-CR to single CR, others where converted into one
1921 // blank. Then InsertStringAsLines is called
1922 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1924 string linestr(str);
1925 bool newline_inserted = false;
1926 for (string::size_type i = 0; i < linestr.length(); ++i) {
1927 if (linestr[i] == '\n') {
1928 if (newline_inserted) {
1929 // we know that \r will be ignored by
1930 // InsertStringA. Of course, it is a dirty
1931 // trick, but it works...
1932 linestr[i - 1] = '\r';
1936 newline_inserted = true;
1938 } else if (IsPrintable(linestr[i])) {
1939 newline_inserted = false;
1942 insertStringAsLines(bview, linestr);
1946 bool LyXText::gotoNextInset(BufferView * bview,
1947 std::vector<Inset::Code> const & codes,
1948 string const & contents) const
1950 LyXCursor res = cursor;
1953 if (res.pos() < res.par()->size() - 1) {
1954 res.pos(res.pos() + 1);
1956 res.par(res.par()->next());
1960 } while (res.par() &&
1961 !(res.par()->isInset(res.pos())
1962 && (inset = res.par()->getInset(res.pos())) != 0
1963 && find(codes.begin(), codes.end(), inset->lyxCode())
1965 && (contents.empty() ||
1966 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1970 setCursor(bview, res.par(), res.pos(), false);
1977 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1980 LyXCursor tmpcursor;
1984 Row * row = getRow(par, pos, y);
1986 // is there a break one row above
1987 if (row->previous() && row->previous()->par() == row->par()) {
1988 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1989 if (z >= row->pos()) {
1990 // set the dimensions of the row above
1991 y -= row->previous()->height();
1993 refresh_row = row->previous();
1994 status(bview, LyXText::NEED_MORE_REFRESH);
1996 breakAgain(bview, row->previous());
1998 // set the cursor again. Otherwise
1999 // dangling pointers are possible
2000 setCursor(bview, cursor.par(), cursor.pos(),
2001 false, cursor.boundary());
2002 selection.cursor = cursor;
2007 int const tmpheight = row->height();
2008 pos_type const tmplast = rowLast(row);
2012 breakAgain(bview, row);
2013 if (row->height() == tmpheight && rowLast(row) == tmplast)
2014 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2016 status(bview, LyXText::NEED_MORE_REFRESH);
2018 // check the special right address boxes
2019 if (textclasslist.Style(bview->buffer()->params.textclass,
2020 par->getLayout()).margintype
2021 == MARGIN_RIGHT_ADDRESS_BOX)
2029 redoDrawingOfParagraph(bview, tmpcursor);
2032 // set the cursor again. Otherwise dangling pointers are possible
2033 // also set the selection
2035 if (selection.set()) {
2037 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
2038 false, selection.cursor.boundary());
2039 selection.cursor = cursor;
2040 setCursorIntern(bview, selection.start.par(),
2041 selection.start.pos(),
2042 false, selection.start.boundary());
2043 selection.start = cursor;
2044 setCursorIntern(bview, selection.end.par(),
2045 selection.end.pos(),
2046 false, selection.end.boundary());
2047 selection.end = cursor;
2048 setCursorIntern(bview, last_sel_cursor.par(),
2049 last_sel_cursor.pos(),
2050 false, last_sel_cursor.boundary());
2051 last_sel_cursor = cursor;
2054 setCursorIntern(bview, cursor.par(), cursor.pos(),
2055 false, cursor.boundary());
2059 // returns false if inset wasn't found
2060 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2062 // first check the current paragraph
2063 int pos = cursor.par()->getPositionOfInset(inset);
2065 checkParagraph(bview, cursor.par(), pos);
2069 // check every paragraph
2071 Paragraph * par = ownerParagraph();
2073 pos = par->getPositionOfInset(inset);
2075 checkParagraph(bview, par, pos);
2085 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2087 bool setfont, bool boundary) const
2089 LyXCursor old_cursor = cursor;
2090 setCursorIntern(bview, par, pos, setfont, boundary);
2091 return deleteEmptyParagraphMechanism(bview, old_cursor);
2095 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2096 pos_type pos, bool boundary) const
2103 cur.boundary(boundary);
2105 // get the cursor y position in text
2107 Row * row = getRow(par, pos, y);
2108 // y is now the beginning of the cursor row
2109 y += row->baseline();
2110 // y is now the cursor baseline
2113 // now get the cursors x position
2115 float fill_separator;
2117 float fill_label_hfill;
2118 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2120 pos_type cursor_vpos = 0;
2121 pos_type last = rowLastPrintable(row);
2123 if (pos > last + 1) {
2124 // This shouldn't happen.
2127 } else if (pos < row->pos()) {
2132 if (last < row->pos())
2133 cursor_vpos = row->pos();
2134 else if (pos > last && !boundary)
2135 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2136 ? row->pos() : last + 1;
2137 else if (pos > row->pos() &&
2138 (pos > last || boundary))
2139 /// Place cursor after char at (logical) position pos - 1
2140 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2141 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2143 /// Place cursor before char at (logical) position pos
2144 cursor_vpos = (bidi_level(pos) % 2 == 0)
2145 ? log2vis(pos) : log2vis(pos) + 1;
2147 pos_type main_body =
2148 beginningOfMainBody(bview->buffer(), row->par());
2149 if ((main_body > 0) &&
2150 ((main_body-1 > last) ||
2151 !row->par()->isLineSeparator(main_body-1)))
2154 for (pos_type vpos = row->pos();
2155 vpos < cursor_vpos; ++vpos) {
2156 pos = vis2log(vpos);
2157 if (main_body > 0 && pos == main_body - 1) {
2158 x += fill_label_hfill +
2159 lyxfont::width(textclasslist.Style(
2160 bview->buffer()->params.textclass,
2161 row->par()->getLayout())
2163 getLabelFont(bview->buffer(), row->par()));
2164 if (row->par()->isLineSeparator(main_body-1))
2165 x -= singleWidth(bview, row->par(),main_body-1);
2167 if (hfillExpansion(bview->buffer(), row, pos)) {
2168 x += singleWidth(bview, row->par(), pos);
2169 if (pos >= main_body)
2172 x += fill_label_hfill;
2173 } else if (row->par()->isSeparator(pos)) {
2174 x += singleWidth(bview, row->par(), pos);
2175 if (pos >= main_body)
2176 x += fill_separator;
2178 x += singleWidth(bview, row->par(), pos);
2187 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2188 pos_type pos, bool setfont, bool boundary) const
2190 InsetText * it = static_cast<InsetText *>(par->inInset());
2192 if (it != inset_owner) {
2193 lyxerr << "InsetText is " << it << endl;
2194 lyxerr << "inset_owner is " << inset_owner << endl;
2195 #warning I believe this code is wrong. (Lgb)
2196 #warning Jürgen, have a look at this. (Lgb)
2197 #warning Hmmm, I guess you are right but we
2198 #warning should verify when this is needed
2199 // Jürgen, would you like to have a look?
2200 // I guess we need to move the outer cursor
2201 // and open and lock the inset (bla bla bla)
2202 // stuff I don't know... so can you have a look?
2204 // I moved the lyxerr stuff in here so we can see if
2205 // this is actually really needed and where!
2207 // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2212 setCursor(bview, cursor, par, pos, boundary);
2214 setCurrentFont(bview);
2218 void LyXText::setCurrentFont(BufferView * bview) const
2220 pos_type pos = cursor.pos();
2221 if (cursor.boundary() && pos > 0)
2225 if (pos == cursor.par()->size())
2227 else // potentional bug... BUG (Lgb)
2228 if (cursor.par()->isSeparator(pos)) {
2229 if (pos > cursor.row()->pos() &&
2230 bidi_level(pos) % 2 ==
2231 bidi_level(pos - 1) % 2)
2233 else if (pos + 1 < cursor.par()->size())
2239 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2240 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2242 if (cursor.pos() == cursor.par()->size() &&
2243 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2244 !cursor.boundary()) {
2245 Language const * lang =
2246 cursor.par()->getParLanguage(bview->buffer()->params);
2247 current_font.setLanguage(lang);
2248 current_font.setNumber(LyXFont::OFF);
2249 real_current_font.setLanguage(lang);
2250 real_current_font.setNumber(LyXFont::OFF);
2255 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2257 LyXCursor old_cursor = cursor;
2259 setCursorFromCoordinates(bview, cursor, x, y);
2260 setCurrentFont(bview);
2261 deleteEmptyParagraphMechanism(bview, old_cursor);
2265 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2268 // Get the row first.
2270 Row * row = getRowNearY(y);
2272 pos_type const column = getColumnNearX(bview, row, x, bound);
2273 cur.par(row->par());
2274 cur.pos(row->pos() + column);
2276 cur.y(y + row->baseline());
2278 cur.boundary(bound);
2282 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2284 if (cursor.pos() > 0) {
2285 bool boundary = cursor.boundary();
2286 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2287 if (!internal && !boundary &&
2288 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2289 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2290 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2291 Paragraph * par = cursor.par()->previous();
2292 setCursor(bview, par, par->size());
2297 void LyXText::cursorRight(BufferView * bview, bool internal) const
2299 if (!internal && cursor.boundary() &&
2300 !cursor.par()->isNewline(cursor.pos()))
2301 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2302 else if (cursor.pos() < cursor.par()->size()) {
2303 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2305 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2306 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2307 } else if (cursor.par()->next())
2308 setCursor(bview, cursor.par()->next(), 0);
2312 void LyXText::cursorUp(BufferView * bview) const
2314 setCursorFromCoordinates(bview, cursor.x_fix(),
2315 cursor.y() - cursor.row()->baseline() - 1);
2319 void LyXText::cursorDown(BufferView * bview) const
2321 setCursorFromCoordinates(bview, cursor.x_fix(),
2322 cursor.y() - cursor.row()->baseline()
2323 + cursor.row()->height() + 1);
2327 void LyXText::cursorUpParagraph(BufferView * bview) const
2329 if (cursor.pos() > 0) {
2330 setCursor(bview, cursor.par(), 0);
2332 else if (cursor.par()->previous()) {
2333 setCursor(bview, cursor.par()->previous(), 0);
2338 void LyXText::cursorDownParagraph(BufferView * bview) const
2340 if (cursor.par()->next()) {
2341 setCursor(bview, cursor.par()->next(), 0);
2343 setCursor(bview, cursor.par(), cursor.par()->size());
2347 // fix the cursor `cur' after a characters has been deleted at `where'
2348 // position. Called by deleteEmptyParagraphMechanism
2349 void LyXText::fixCursorAfterDelete(BufferView * bview,
2351 LyXCursor const & where) const
2353 // if cursor is not in the paragraph where the delete occured,
2355 if (cur.par() != where.par())
2358 // if cursor position is after the place where the delete occured,
2360 if (cur.pos() > where.pos())
2361 cur.pos(cur.pos()-1);
2363 // recompute row et al. for this cursor
2364 setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2368 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2369 LyXCursor const & old_cursor) const
2371 // Would be wrong to delete anything if we have a selection.
2372 if (selection.set()) return false;
2374 // We allow all kinds of "mumbo-jumbo" when freespacing.
2375 if (textclasslist.Style(bview->buffer()->params.textclass,
2376 old_cursor.par()->getLayout()).free_spacing
2377 || old_cursor.par()->isFreeSpacing())
2382 /* Ok I'll put some comments here about what is missing.
2383 I have fixed BackSpace (and thus Delete) to not delete
2384 double-spaces automagically. I have also changed Cut,
2385 Copy and Paste to hopefully do some sensible things.
2386 There are still some small problems that can lead to
2387 double spaces stored in the document file or space at
2388 the beginning of paragraphs. This happens if you have
2389 the cursor betwenn to spaces and then save. Or if you
2390 cut and paste and the selection have a space at the
2391 beginning and then save right after the paste. I am
2392 sure none of these are very hard to fix, but I will
2393 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2394 that I can get some feedback. (Lgb)
2397 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2398 // delete the LineSeparator.
2401 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2402 // delete the LineSeparator.
2405 // If the pos around the old_cursor were spaces, delete one of them.
2406 if (old_cursor.par() != cursor.par()
2407 || old_cursor.pos() != cursor.pos()) {
2408 // Only if the cursor has really moved
2410 if (old_cursor.pos() > 0
2411 && old_cursor.pos() < old_cursor.par()->size()
2412 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2413 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2414 old_cursor.par()->erase(old_cursor.pos() - 1);
2415 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2417 #ifdef WITH_WARNINGS
2418 #warning This will not work anymore when we have multiple views of the same buffer
2419 // In this case, we will have to correct also the cursors held by
2420 // other bufferviews. It will probably be easier to do that in a more
2421 // automated way in LyXCursor code. (JMarc 26/09/2001)
2423 // correct all cursors held by the LyXText
2424 fixCursorAfterDelete(bview, cursor, old_cursor);
2425 fixCursorAfterDelete(bview, selection.cursor,
2427 fixCursorAfterDelete(bview, selection.start,
2429 fixCursorAfterDelete(bview, selection.end, old_cursor);
2430 fixCursorAfterDelete(bview, last_sel_cursor,
2432 fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2433 fixCursorAfterDelete(bview, toggle_end_cursor,
2439 // don't delete anything if this is the ONLY paragraph!
2440 if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2443 // Do not delete empty paragraphs with keepempty set.
2444 if ((textclasslist.Style(bview->buffer()->params.textclass,
2445 old_cursor.par()->getLayout())).keepempty)
2448 // only do our magic if we changed paragraph
2449 if (old_cursor.par() == cursor.par())
2452 // record if we have deleted a paragraph
2453 // we can't possibly have deleted a paragraph before this point
2454 bool deleted = false;
2456 if ((old_cursor.par()->size() == 0
2457 || (old_cursor.par()->size() == 1
2458 && old_cursor.par()->isLineSeparator(0)))) {
2459 // ok, we will delete anything
2460 LyXCursor tmpcursor;
2462 // make sure that you do not delete any environments
2463 status(bview, LyXText::NEED_MORE_REFRESH);
2466 if (old_cursor.row()->previous()) {
2467 refresh_row = old_cursor.row()->previous();
2468 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2470 cursor = old_cursor; // that undo can restore the right cursor position
2471 Paragraph * endpar = old_cursor.par()->next();
2472 if (endpar && endpar->getDepth()) {
2473 while (endpar && endpar->getDepth()) {
2474 endpar = endpar->next();
2477 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2481 removeRow(old_cursor.row());
2482 if (ownerParagraph() == old_cursor.par()) {
2483 ownerParagraph(ownerParagraph()->next());
2486 delete old_cursor.par();
2488 /* Breakagain the next par. Needed because of
2489 * the parindent that can occur or dissappear.
2490 * The next row can change its height, if
2491 * there is another layout before */
2492 if (refresh_row->next()) {
2493 breakAgain(bview, refresh_row->next());
2494 updateCounters(bview, refresh_row);
2496 setHeightOfRow(bview, refresh_row);
2498 refresh_row = old_cursor.row()->next();
2499 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2502 cursor = old_cursor; // that undo can restore the right cursor position
2503 Paragraph * endpar = old_cursor.par()->next();
2504 if (endpar && endpar->getDepth()) {
2505 while (endpar && endpar->getDepth()) {
2506 endpar = endpar->next();
2509 setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2513 removeRow(old_cursor.row());
2515 if (ownerParagraph() == old_cursor.par()) {
2516 ownerParagraph(ownerParagraph()->next());
2519 delete old_cursor.par();
2521 /* Breakagain the next par. Needed because of
2522 the parindent that can occur or dissappear.
2523 The next row can change its height, if
2524 there is another layout before */
2526 breakAgain(bview, refresh_row);
2527 updateCounters(bview, refresh_row->previous());
2532 setCursorIntern(bview, cursor.par(), cursor.pos());
2534 if (selection.cursor.par() == old_cursor.par()
2535 && selection.cursor.pos() == old_cursor.pos()) {
2536 // correct selection
2537 selection.cursor = cursor;
2541 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2542 redoParagraphs(bview, old_cursor,
2543 old_cursor.par()->next());
2545 setCursorIntern(bview, cursor.par(), cursor.pos());
2546 selection.cursor = cursor;
2553 void LyXText::toggleAppendix(BufferView * bview)
2555 Paragraph * par = cursor.par();
2556 bool start = !par->params().startOfAppendix();
2558 // ensure that we have only one start_of_appendix in this document
2559 Paragraph * tmp = ownerParagraph();
2560 for (; tmp; tmp = tmp->next()) {
2561 tmp->params().startOfAppendix(false);
2564 par->params().startOfAppendix(start);
2566 // we can set the refreshing parameters now
2567 status(bview, LyXText::NEED_MORE_REFRESH);
2569 refresh_row = 0; // not needed for full update
2570 updateCounters(bview, 0);
2571 setCursor(bview, cursor.par(), cursor.pos());
2575 Paragraph * LyXText::ownerParagraph() const
2578 return inset_owner->paragraph();
2580 return bv_owner->buffer()->paragraph;
2584 void LyXText::ownerParagraph(Paragraph * p) const
2587 inset_owner->paragraph(p);
2589 bv_owner->buffer()->paragraph = p;
2594 void LyXText::ownerParagraph(int id, Paragraph * p) const
2596 Paragraph * op = bv_owner->buffer()->getParFromID(id);
2597 if (op && op->inInset()) {
2598 static_cast<InsetText *>(op->inInset())->paragraph(p);
2605 LyXText::text_status LyXText::status() const
2611 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2613 // well as much as I know && binds more then || so the above and the
2614 // below are identical (this for your known use of parentesis!)
2615 // Now some explanation:
2616 // We should only go up with refreshing code so this means that if
2617 // we have a MORE refresh we should never set it to LITTLE if we still
2618 // didn't handle it (and then it will be UNCHANGED. Now as long as
2619 // we stay inside one LyXText this may work but we need to tell the
2620 // outermost LyXText that it should REALLY draw us if there is some
2621 // change in a Inset::LyXText. So you see that when we are inside a
2622 // inset's LyXText we give the LITTLE to the outermost LyXText to
2623 // tell'em that it should redraw the actual row (where the inset
2624 // resides! Capito?!
2626 if ((status_ != NEED_MORE_REFRESH)
2627 || (status_ == NEED_MORE_REFRESH
2628 && st != NEED_VERY_LITTLE_REFRESH))
2631 if (inset_owner && st != UNCHANGED) {
2632 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2633 if (!bview->text->refresh_row) {
2634 bview->text->refresh_row = bview->text->cursor.row();
2635 bview->text->refresh_y = bview->text->cursor.y() -
2636 bview->text->cursor.row()->baseline();