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 "undo_funcs.h"
30 #include "bufferparams.h"
31 #include "lyx_gui_misc.h"
33 #include "BufferView.h"
35 #include "CutAndPaste.h"
40 #include "FloatList.h"
42 #include "ParagraphParameters.h"
51 LyXText::LyXText(BufferView * bv)
52 : number_of_rows(0), height(0), width(0), first(0),
53 bv_owner(bv), inset_owner(0), the_locking_inset(0),
54 need_break_row(0), refresh_y(0), status_(LyXText::UNCHANGED),
55 firstrow(0), lastrow(0), copylayouttype(0)
59 LyXText::LyXText(InsetText * inset)
60 : number_of_rows(0), height(0), width(0), first(0),
61 bv_owner(0), inset_owner(inset), the_locking_inset(0),
62 need_break_row(0), refresh_y(0), status_(LyXText::UNCHANGED),
63 firstrow(0), lastrow(0), copylayouttype(0)
66 void LyXText::init(BufferView * bview)
71 Paragraph * par = ownerParagraph();
72 current_font = getFont(bview->buffer(), par, 0);
74 insertParagraph(bview, par, lastrow);
77 setCursorIntern(bview, firstrow->par(), 0);
78 selection.cursor = cursor;
84 // Delete all rows, this does not touch the paragraphs!
85 Row * tmprow = firstrow;
87 tmprow = firstrow->next();
94 // Gets the fully instantiated font at a given position in a paragraph
95 // Basically the same routine as Paragraph::getFont() in paragraph.C.
96 // The difference is that this one is used for displaying, and thus we
97 // are allowed to make cosmetic improvements. For instance make footnotes
99 // If position is -1, we get the layout font of the paragraph.
100 // If position is -2, we get the font of the manual label of the paragraph.
101 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
102 Paragraph::size_type pos) const
104 LyXLayout const & layout =
105 textclasslist.Style(buf->params.textclass, par->getLayout());
107 Paragraph::depth_type par_depth = par->getDepth();
108 // We specialize the 95% common case:
112 if (layout.labeltype == LABEL_MANUAL
113 && pos < beginningOfMainBody(buf, par)) {
115 LyXFont f = par->getFontSettings(buf->params,
117 return f.realize(layout.reslabelfont);
119 LyXFont f = par->getFontSettings(buf->params, pos);
120 return f.realize(layout.resfont);
125 // process layoutfont for pos == -1 and labelfont for pos < -1
127 return layout.resfont;
129 return layout.reslabelfont;
133 // The uncommon case need not be optimized as much
135 LyXFont layoutfont, tmpfont;
139 if (pos < beginningOfMainBody(buf, par)) {
141 layoutfont = layout.labelfont;
144 layoutfont = layout.font;
146 tmpfont = par->getFontSettings(buf->params, pos);
147 tmpfont.realize(layoutfont);
150 // process layoutfont for pos == -1 and labelfont for pos < -1
152 tmpfont = layout.font;
154 tmpfont = layout.labelfont;
157 // Resolve against environment font information
158 while (par && par_depth && !tmpfont.resolved()) {
159 par = par->outerHook();
161 tmpfont.realize(textclasslist.
162 Style(buf->params.textclass,
163 par->getLayout()).font);
164 par_depth = par->getDepth();
168 tmpfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
174 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
175 Paragraph::size_type pos, LyXFont const & fnt,
178 Buffer const * buf = bv->buffer();
179 LyXFont font = getFont(buf, par, pos);
180 font.update(fnt, buf->params.language, toggleall);
181 // Let the insets convert their font
182 if (par->getChar(pos) == Paragraph::META_INSET) {
183 Inset * inset = par->getInset(pos);
185 if (inset->editable()==Inset::HIGHLY_EDITABLE) {
186 UpdatableInset * uinset = static_cast<UpdatableInset *>(inset);
187 uinset->setFont(bv, fnt, toggleall, true);
189 font = inset->convertFont(font);
193 LyXLayout const & layout =
194 textclasslist.Style(buf->params.textclass,
197 // Get concrete layout font to reduce against
200 if (pos < beginningOfMainBody(buf, par))
201 layoutfont = layout.labelfont;
203 layoutfont = layout.font;
205 // Realize against environment font information
206 if (par->getDepth()){
207 Paragraph * tp = par;
208 while (!layoutfont.resolved() && tp && tp->getDepth()) {
209 tp = tp->outerHook();
211 layoutfont.realize(textclasslist.
212 Style(buf->params.textclass,
213 tp->getLayout()).font);
217 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
219 // Now, reduce font against full layout font
220 font.reduce(layoutfont);
222 par->setFont(pos, font);
226 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
227 Paragraph::size_type pos, LyXFont const & fnt)
230 // Let the insets convert their font
231 if (par->getChar(pos) == Paragraph::META_INSET) {
232 font = par->getInset(pos)->convertFont(font);
235 LyXLayout const & layout =
236 textclasslist.Style(buf->params.textclass,
239 // Get concrete layout font to reduce against
242 if (pos < beginningOfMainBody(buf, par))
243 layoutfont = layout.labelfont;
245 layoutfont = layout.font;
247 // Realize against environment font information
248 if (par->getDepth()){
249 Paragraph * tp = par;
250 while (!layoutfont.resolved() && tp && tp->getDepth()) {
251 tp = tp->outerHook();
253 layoutfont.realize(textclasslist.
254 Style(buf->params.textclass,
255 tp->getLayout()).font);
259 layoutfont.realize(textclasslist.TextClass(buf->params.textclass).defaultfont());
261 // Now, reduce font against full layout font
262 font.reduce(layoutfont);
264 par->setFont(pos, font);
268 /* inserts a new row behind the specified row, increments
269 * the touched counters */
270 void LyXText::insertRow(Row * row, Paragraph * par,
271 Paragraph::size_type pos) const
273 Row * tmprow = new Row;
276 tmprow->next(firstrow);
279 tmprow->previous(row);
280 tmprow->next(row->next());
285 tmprow->next()->previous(tmprow);
287 if (tmprow->previous())
288 tmprow->previous()->next(tmprow);
296 ++number_of_rows; // one more row
300 // removes the row and reset the touched counters
301 void LyXText::removeRow(Row * row) const
303 /* this must not happen before the currentrow for clear reasons.
304 so the trick is just to set the current row onto the previous
307 getRow(row->par(), row->pos(), unused_y);
310 row->next()->previous(row->previous());
311 if (!row->previous()) {
312 firstrow = row->next();
314 row->previous()->next(row->next());
317 lastrow = row->previous();
319 height -= row->height(); // the text becomes smaller
322 --number_of_rows; // one row less
326 // remove all following rows of the paragraph of the specified row.
327 void LyXText::removeParagraph(Row * row) const
329 Paragraph * tmppar = row->par();
333 while (row && row->par() == tmppar) {
334 tmprow = row->next();
341 // insert the specified paragraph behind the specified row
342 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
345 insertRow(row, par, 0); /* insert a new row, starting
348 setCounter(bview->buffer(), par); // set the counters
350 // and now append the whole paragraph behind the new row
353 appendParagraph(bview, firstrow);
355 row->next()->height(0);
356 appendParagraph(bview, row->next());
361 /* used in setlayout */
362 // Asger is not sure we want to do this...
363 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
367 LyXLayout const & layout =
368 textclasslist.Style(buf->params.textclass, par->getLayout());
370 LyXFont layoutfont, tmpfont;
371 for (Paragraph::size_type pos = 0;
372 pos < par->size(); ++pos) {
373 if (pos < beginningOfMainBody(buf, par))
374 layoutfont = layout.labelfont;
376 layoutfont = layout.font;
378 tmpfont = par->getFontSettings(buf->params, pos);
379 tmpfont.reduce(layoutfont);
380 par->setFont(pos, tmpfont);
385 Paragraph * LyXText::setLayout(BufferView * bview,
386 LyXCursor & cur, LyXCursor & sstart_cur,
387 LyXCursor & send_cur,
388 LyXTextClass::size_type layout)
390 Paragraph * endpar = send_cur.par()->next();
391 Paragraph * undoendpar = endpar;
393 if (endpar && endpar->getDepth()) {
394 while (endpar && endpar->getDepth()) {
395 endpar = endpar->next();
399 endpar = endpar->next(); // because of parindents etc.
402 setUndo(bview, Undo::EDIT,
403 sstart_cur.par(), undoendpar);
405 /* ok we have a selection. This is always between sstart_cur
406 * and sel_end cursor */
409 LyXLayout const & lyxlayout =
410 textclasslist.Style(bview->buffer()->params.textclass, layout);
412 while (cur.par() != send_cur.par()) {
413 cur.par()->setLayout(layout);
414 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
415 Paragraph * fppar = cur.par();
416 fppar->params().spaceTop(lyxlayout.fill_top ?
417 VSpace(VSpace::VFILL)
418 : VSpace(VSpace::NONE));
419 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
420 VSpace(VSpace::VFILL)
421 : VSpace(VSpace::NONE));
422 if (lyxlayout.margintype == MARGIN_MANUAL)
423 cur.par()->setLabelWidthString(lyxlayout.labelstring());
424 if (lyxlayout.labeltype != LABEL_BIBLIO
426 delete fppar->bibkey;
429 cur.par(cur.par()->next());
431 cur.par()->setLayout(layout);
432 makeFontEntriesLayoutSpecific(bview->buffer(), cur.par());
433 Paragraph * fppar = cur.par();
434 fppar->params().spaceTop(lyxlayout.fill_top ?
435 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
436 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
437 VSpace(VSpace::VFILL) : VSpace(VSpace::NONE));
438 if (lyxlayout.margintype == MARGIN_MANUAL)
439 cur.par()->setLabelWidthString(lyxlayout.labelstring());
440 if (lyxlayout.labeltype != LABEL_BIBLIO
442 delete fppar->bibkey;
449 // set layout over selection and make a total rebreak of those paragraphs
450 void LyXText::setLayout(BufferView * bview, LyXTextClass::size_type layout)
452 LyXCursor tmpcursor = cursor; /* store the current cursor */
454 // if there is no selection just set the layout
455 // of the current paragraph */
456 if (!selection.set()) {
457 selection.start = cursor; // dummy selection
458 selection.end = cursor;
460 Paragraph * endpar = setLayout(bview, cursor, selection.start,
461 selection.end, layout);
462 redoParagraphs(bview, selection.start, endpar);
464 // we have to reset the selection, because the
465 // geometry could have changed
466 setCursor(bview, selection.start.par(),
467 selection.start.pos(), false);
468 selection.cursor = cursor;
469 setCursor(bview, selection.end.par(), selection.end.pos(),
471 updateCounters(bview, cursor.row());
472 clearSelection(bview);
474 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
478 // increment depth over selection and
479 // make a total rebreak of those paragraphs
480 void LyXText::incDepth(BufferView * bview)
482 // If there is no selection, just use the current paragraph
483 if (!selection.set()) {
484 selection.start = cursor; // dummy selection
485 selection.end = cursor;
488 // We end at the next paragraph with depth 0
489 Paragraph * endpar = selection.end.par()->next();
491 Paragraph * undoendpar = endpar;
493 if (endpar && endpar->getDepth()) {
494 while (endpar && endpar->getDepth()) {
495 endpar = endpar->next();
500 endpar = endpar->next(); // because of parindents etc.
503 setUndo(bview, Undo::EDIT,
504 selection.start.par(), undoendpar);
506 LyXCursor tmpcursor = cursor; // store the current cursor
508 // ok we have a selection. This is always between sel_start_cursor
509 // and sel_end cursor
510 cursor = selection.start;
512 bool anything_changed = false;
515 // NOTE: you can't change the depth of a bibliography entry
517 textclasslist.Style(bview->buffer()->params.textclass,
518 cursor.par()->getLayout()
519 ).labeltype != LABEL_BIBLIO) {
520 Paragraph * prev = cursor.par()->previous();
523 && (prev->getDepth() - cursor.par()->getDepth() > 0
524 || (prev->getDepth() == cursor.par()->getDepth()
525 && textclasslist.Style(bview->buffer()->params.textclass,
526 prev->getLayout()).isEnvironment()))) {
527 cursor.par()->params().depth(cursor.par()->params().depth() + 1);
528 anything_changed = true;
531 if (cursor.par() == selection.end.par())
533 cursor.par(cursor.par()->next());
536 // if nothing changed set all depth to 0
537 if (!anything_changed) {
538 cursor = selection.start;
539 while (cursor.par() != selection.end.par()) {
540 cursor.par()->params().depth(0);
541 cursor.par(cursor.par()->next());
543 cursor.par()->params().depth(0);
546 redoParagraphs(bview, selection.start, endpar);
548 // we have to reset the selection, because the
549 // geometry could have changed
550 setCursor(bview, selection.start.par(), selection.start.pos());
551 selection.cursor = cursor;
552 setCursor(bview, selection.end.par(), selection.end.pos());
553 updateCounters(bview, cursor.row());
554 clearSelection(bview);
556 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
560 // decrement depth over selection and
561 // make a total rebreak of those paragraphs
562 void LyXText::decDepth(BufferView * bview)
564 // if there is no selection just set the layout
565 // of the current paragraph
566 if (!selection.set()) {
567 selection.start = cursor; // dummy selection
568 selection.end = cursor;
570 Paragraph * endpar = selection.end.par()->next();
571 Paragraph * undoendpar = endpar;
573 if (endpar && endpar->getDepth()) {
574 while (endpar && endpar->getDepth()) {
575 endpar = endpar->next();
579 endpar = endpar->next(); // because of parindents etc.
582 setUndo(bview, Undo::EDIT,
583 selection.start.par(), undoendpar);
585 LyXCursor tmpcursor = cursor; // store the current cursor
587 // ok we have a selection. This is always between sel_start_cursor
588 // and sel_end cursor
589 cursor = selection.start;
592 if (cursor.par()->params().depth())
593 cursor.par()->params().depth(cursor.par()->params().depth() - 1);
594 if (cursor.par() == selection.end.par())
596 cursor.par(cursor.par()->next());
599 redoParagraphs(bview, selection.start, endpar);
601 // we have to reset the selection, because the
602 // geometry could have changed
603 setCursor(bview, selection.start.par(),
604 selection.start.pos());
605 selection.cursor = cursor;
606 setCursor(bview, selection.end.par(), selection.end.pos());
607 updateCounters(bview, cursor.row());
608 clearSelection(bview);
610 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
614 // set font over selection and make a total rebreak of those paragraphs
615 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
617 // if there is no selection just set the current_font
618 if (!selection.set()) {
619 // Determine basis font
621 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
623 layoutfont = getFont(bview->buffer(), cursor.par(),-2);
625 layoutfont = getFont(bview->buffer(), cursor.par(),-1);
626 // Update current font
627 real_current_font.update(font,
628 bview->buffer()->params.language,
631 // Reduce to implicit settings
632 current_font = real_current_font;
633 current_font.reduce(layoutfont);
634 // And resolve it completely
635 real_current_font.realize(layoutfont);
639 LyXCursor tmpcursor = cursor; // store the current cursor
641 // ok we have a selection. This is always between sel_start_cursor
642 // and sel_end cursor
644 setUndo(bview, Undo::EDIT,
645 selection.start.par(),
646 selection.end.par()->next());
648 cursor = selection.start;
649 while (cursor.par() != selection.end.par() ||
650 (cursor.pos() < selection.end.pos())) {
651 if (cursor.pos() < cursor.par()->size()) {
652 // an open footnote should behave
654 setCharFont(bview, cursor.par(), cursor.pos(), font, toggleall);
655 cursor.pos(cursor.pos() + 1);
658 cursor.par(cursor.par()->next());
663 redoParagraphs(bview, selection.start, selection.end.par()->next());
665 // we have to reset the selection, because the
666 // geometry could have changed
667 setCursor(bview, selection.start.par(), selection.start.pos());
668 selection.cursor = cursor;
669 setCursor(bview, selection.end.par(), selection.end.pos());
670 clearSelection(bview);
672 setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
673 tmpcursor.boundary());
677 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
679 Row * tmprow = cur.row();
680 int y = cur.y() - tmprow->baseline();
682 setHeightOfRow(bview, tmprow);
685 Paragraph * first_phys_par = tmprow->par();
687 // find the first row of the paragraph
688 if (first_phys_par != tmprow->par())
689 while (tmprow->previous()
690 && tmprow->previous()->par() != first_phys_par) {
691 tmprow = tmprow->previous();
692 y -= tmprow->height();
693 setHeightOfRow(bview, tmprow);
695 while (tmprow->previous() && tmprow->previous()->par() == first_phys_par) {
696 tmprow = tmprow->previous();
697 y -= tmprow->height();
698 setHeightOfRow(bview, tmprow);
701 while (tmprow->previous() && tmprow->previous()->par() == tmprow->par()) {
702 tmprow = tmprow->previous();
703 y -= tmprow->height();
704 setHeightOfRow(bview, tmprow);
708 // we can set the refreshing parameters now
709 status(bview, LyXText::NEED_MORE_REFRESH);
711 refresh_row = tmprow;
712 setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
716 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
718 Row * tmprow = cur.row();
720 int y = cur.y() - tmprow->baseline();
721 setHeightOfRow(bview, tmprow);
724 Paragraph * first_phys_par = tmprow->par();
726 // find the first row of the paragraph
727 if (first_phys_par != tmprow->par())
728 while (tmprow->previous() && tmprow->previous()->par() != first_phys_par) {
729 tmprow = tmprow->previous();
730 y -= tmprow->height();
732 while (tmprow->previous() && tmprow->previous()->par() == first_phys_par) {
733 tmprow = tmprow->previous();
734 y -= tmprow->height();
737 while (tmprow->previous() && tmprow->previous()->par() == tmprow->par()) {
738 tmprow = tmprow->previous();
739 y -= tmprow->height();
742 // we can set the refreshing parameters now
743 if (status_ == LyXText::UNCHANGED || y < refresh_y) {
745 refresh_row = tmprow;
747 status(bview, LyXText::NEED_MORE_REFRESH);
748 setCursor(bview, cur.par(), cur.pos());
752 /* deletes and inserts again all paragaphs between the cursor
753 * and the specified par
754 * This function is needed after SetLayout and SetFont etc. */
755 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
756 Paragraph const * endpar) const
759 Paragraph * tmppar = 0;
760 Paragraph * first_phys_par = 0;
762 Row * tmprow = cur.row();
764 int y = cur.y() - tmprow->baseline();
767 if (!tmprow->previous()) {
768 first_phys_par = firstParagraph(); // a trick/hack for UNDO
770 first_phys_par = tmprow->par();
771 // find the first row of the paragraph
772 if (first_phys_par != tmprow->par())
773 while (tmprow->previous() &&
774 (tmprow->previous()->par() != first_phys_par)) {
775 tmprow = tmprow->previous();
776 y -= tmprow->height();
778 while (tmprow->previous()
779 && tmprow->previous()->par() == first_phys_par) {
780 tmprow = tmprow->previous();
781 y -= tmprow->height();
785 if (!tmprow->previous()) {
786 // a trick/hack for UNDO
787 // Can somebody please tell me _why_ this solves
789 first_phys_par = firstParagraph();
791 first_phys_par = tmprow->par();
792 while (tmprow->previous()
793 && tmprow->previous()->par() == first_phys_par) {
794 tmprow = tmprow->previous();
795 y -= tmprow->height();
800 // we can set the refreshing parameters now
801 status(bview, LyXText::NEED_MORE_REFRESH);
803 refresh_row = tmprow->previous(); /* the real refresh row will
804 be deleted, so I store
808 tmppar = tmprow->next()->par();
811 while (tmppar != endpar) {
812 removeRow(tmprow->next());
814 tmppar = tmprow->next()->par();
819 // remove the first one
820 tmprow2 = tmprow; /* this is because tmprow->previous()
822 tmprow = tmprow->previous();
825 tmppar = first_phys_par;
829 insertParagraph(bview, tmppar, tmprow);
832 while (tmprow->next() && tmprow->next()->par() == tmppar)
833 tmprow = tmprow->next();
834 tmppar = tmppar->next();
836 } while (tmppar && tmppar != endpar);
838 // this is because of layout changes
840 refresh_y -= refresh_row->height();
841 setHeightOfRow(bview, refresh_row);
843 refresh_row = firstrow;
845 setHeightOfRow(bview, refresh_row);
848 if (tmprow && tmprow->next())
849 setHeightOfRow(bview, tmprow->next());
853 bool LyXText::fullRebreak(BufferView * bview)
859 if (need_break_row) {
860 breakAgain(bview, need_break_row);
868 /* important for the screen */
871 /* the cursor set functions have a special mechanism. When they
872 * realize, that you left an empty paragraph, they will delete it.
873 * They also delete the corresponding row */
875 // need the selection cursor:
876 void LyXText::setSelection(BufferView * bview)
878 bool const lsel = selection.set();
880 if (!selection.set()) {
881 last_sel_cursor = selection.cursor;
882 selection.start = selection.cursor;
883 selection.end = selection.cursor;
888 // first the toggling area
889 if (cursor.y() < last_sel_cursor.y()
890 || (cursor.y() == last_sel_cursor.y()
891 && cursor.x() < last_sel_cursor.x())) {
892 toggle_end_cursor = last_sel_cursor;
893 toggle_cursor = cursor;
895 toggle_end_cursor = cursor;
896 toggle_cursor = last_sel_cursor;
899 last_sel_cursor = cursor;
901 // and now the whole selection
903 if (selection.cursor.par() == cursor.par())
904 if (selection.cursor.pos() < cursor.pos()) {
905 selection.end = cursor;
906 selection.start = selection.cursor;
908 selection.end = selection.cursor;
909 selection.start = cursor;
911 else if (selection.cursor.y() < cursor.y() ||
912 (selection.cursor.y() == cursor.y() && selection.cursor.x() < cursor.x())) {
913 selection.end = cursor;
914 selection.start = selection.cursor;
917 selection.end = selection.cursor;
918 selection.start = cursor;
921 // a selection with no contents is not a selection
922 if (selection.start.par() == selection.end.par() &&
923 selection.start.pos() == selection.end.pos())
924 selection.set(false);
926 if (inset_owner && (selection.set() || lsel))
927 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
931 string const LyXText::selectionAsString(Buffer const * buffer) const
933 if (!selection.set()) return string();
936 // Special handling if the whole selection is within one paragraph
937 if (selection.start.par() == selection.end.par()) {
938 result += selection.start.par()->asString(buffer,
939 selection.start.pos(),
940 selection.end.pos());
944 // The selection spans more than one paragraph
946 // First paragraph in selection
947 result += selection.start.par()->asString(buffer,
948 selection.start.pos(),
949 selection.start.par()->size())
952 // The paragraphs in between (if any)
953 LyXCursor tmpcur(selection.start);
954 tmpcur.par(tmpcur.par()->next());
955 while (tmpcur.par() != selection.end.par()) {
956 result += tmpcur.par()->asString(buffer, 0, tmpcur.par()->size()) + "\n\n";
957 tmpcur.par(tmpcur.par()->next()); // Or NextAfterFootnote??
960 // Last paragraph in selection
961 result += selection.end.par()->asString(buffer, 0, selection.end.pos());
967 void LyXText::clearSelection(BufferView * /*bview*/) const
969 selection.set(false);
970 selection.mark(false);
971 selection.end = selection.start = cursor;
975 void LyXText::cursorHome(BufferView * bview) const
977 setCursor(bview, cursor.par(), cursor.row()->pos());
981 void LyXText::cursorEnd(BufferView * bview) const
983 if (!cursor.row()->next() || cursor.row()->next()->par() != cursor.row()->par())
984 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
986 if (cursor.par()->size() &&
987 (cursor.par()->getChar(rowLast(cursor.row())) == ' '
988 || cursor.par()->isNewline(rowLast(cursor.row()))))
989 setCursor(bview, cursor.par(), rowLast(cursor.row()));
991 setCursor(bview,cursor.par(), rowLast(cursor.row()) + 1);
996 void LyXText::cursorTop(BufferView * bview) const
998 while (cursor.par()->previous())
999 cursor.par(cursor.par()->previous());
1000 setCursor(bview, cursor.par(), 0);
1004 void LyXText::cursorBottom(BufferView * bview) const
1006 while (cursor.par()->next())
1007 cursor.par(cursor.par()->next());
1008 setCursor(bview, cursor.par(), cursor.par()->size());
1012 void LyXText::toggleFree(BufferView * bview,
1013 LyXFont const & font, bool toggleall)
1015 // If the mask is completely neutral, tell user
1016 if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1017 // Could only happen with user style
1018 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1022 // Try implicit word selection
1023 // If there is a change in the language the implicit word selection
1025 LyXCursor resetCursor = cursor;
1026 bool implicitSelection = (font.language() == ignore_language
1027 && font.number() == LyXFont::IGNORE)
1028 ? selectWordWhenUnderCursor(bview) : false;
1031 setFont(bview, font, toggleall);
1033 /* Implicit selections are cleared afterwards and cursor is set to the
1034 original position. */
1035 if (implicitSelection) {
1036 clearSelection(bview);
1037 cursor = resetCursor;
1038 setCursor(bview, cursor.par(), cursor.pos());
1039 selection.cursor = cursor;
1042 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1046 Paragraph::size_type
1047 LyXText::beginningOfMainBody(Buffer const * buf,
1048 Paragraph const * par) const
1050 if (textclasslist.Style(buf->params.textclass,
1051 par->getLayout()).labeltype != LABEL_MANUAL)
1054 return par->beginningOfMainBody();
1058 /* the DTP switches for paragraphs. LyX will store them in the
1059 * first physicla paragraph. When a paragraph is broken, the top settings
1060 * rest, the bottom settings are given to the new one. So I can make shure,
1061 * they do not duplicate themself and you cannnot make dirty things with
1064 void LyXText::setParagraph(BufferView * bview,
1065 bool line_top, bool line_bottom,
1066 bool pagebreak_top, bool pagebreak_bottom,
1067 VSpace const & space_top,
1068 VSpace const & space_bottom,
1070 string labelwidthstring,
1073 LyXCursor tmpcursor = cursor;
1074 if (!selection.set()) {
1075 selection.start = cursor;
1076 selection.end = cursor;
1079 // make sure that the depth behind the selection are restored, too
1080 Paragraph * endpar = selection.end.par()->next();
1081 Paragraph * undoendpar = endpar;
1083 if (endpar && endpar->getDepth()) {
1084 while (endpar && endpar->getDepth()) {
1085 endpar = endpar->next();
1086 undoendpar = endpar;
1090 endpar = endpar->next(); // because of parindents etc.
1093 setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1096 Paragraph * tmppar = selection.end.par();
1097 while (tmppar != selection.start.par()->previous()) {
1098 setCursor(bview, tmppar, 0);
1099 status(bview, LyXText::NEED_MORE_REFRESH);
1100 refresh_row = cursor.row();
1101 refresh_y = cursor.y() - cursor.row()->baseline();
1102 cursor.par()->params().lineTop(line_top);
1103 cursor.par()->params().lineBottom(line_bottom);
1104 cursor.par()->params().pagebreakTop(pagebreak_top);
1105 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1106 cursor.par()->params().spaceTop(space_top);
1107 cursor.par()->params().spaceBottom(space_bottom);
1108 // does the layout allow the new alignment?
1109 if (align == LYX_ALIGN_LAYOUT)
1110 align = textclasslist
1111 .Style(bview->buffer()->params.textclass,
1112 cursor.par()->getLayout()).align;
1113 if (align & textclasslist
1114 .Style(bview->buffer()->params.textclass,
1115 cursor.par()->getLayout()).alignpossible) {
1116 if (align == textclasslist
1117 .Style(bview->buffer()->params.textclass,
1118 cursor.par()->getLayout()).align)
1119 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1121 cursor.par()->params().align(align);
1123 cursor.par()->setLabelWidthString(labelwidthstring);
1124 cursor.par()->params().noindent(noindent);
1125 tmppar = cursor.par()->previous();
1128 redoParagraphs(bview, selection.start, endpar);
1130 clearSelection(bview);
1131 setCursor(bview, selection.start.par(), selection.start.pos());
1132 selection.cursor = cursor;
1133 setCursor(bview, selection.end.par(), selection.end.pos());
1134 setSelection(bview);
1135 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1137 bview->updateInset(inset_owner, true);
1141 char loweralphaCounter(int n)
1143 if (n < 1 || n > 26)
1153 char alphaCounter(int n)
1155 if (n < 1 || n > 26)
1163 char hebrewCounter(int n)
1165 static const char hebrew[22] = {
1166 'à ', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è',
1167 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1168 '÷', 'ø', 'ù', 'ú'
1170 if (n < 1 || n > 22)
1178 string const romanCounter(int n)
1180 static char const * roman[20] = {
1181 "i", "ii", "iii", "iv", "v",
1182 "vi", "vii", "viii", "ix", "x",
1183 "xi", "xii", "xiii", "xiv", "xv",
1184 "xvi", "xvii", "xviii", "xix", "xx"
1186 if (n < 1 || n > 20)
1195 // set the counter of a paragraph. This includes the labels
1196 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1198 LyXLayout const & layout =
1199 textclasslist.Style(buf->params.textclass,
1202 LyXTextClass const & textclass =
1203 textclasslist.TextClass(buf->params.textclass);
1205 /* copy the prev-counters to this one, unless this is the start of a
1206 footnote or of a bibliography or the very first paragraph */
1208 && !(textclasslist.Style(buf->params.textclass,
1209 par->previous()->getLayout()
1210 ).labeltype != LABEL_BIBLIO
1211 && layout.labeltype == LABEL_BIBLIO)) {
1212 for (int i = 0; i < 10; ++i) {
1213 par->setCounter(i, par->previous()->getFirstCounter(i));
1215 par->params().appendix(par->previous()->params().appendix());
1216 if (!par->params().appendix() && par->params().startOfAppendix()) {
1217 par->params().appendix(true);
1218 for (int i = 0; i < 10; ++i) {
1219 par->setCounter(i, 0);
1222 par->enumdepth = par->previous()->enumdepth;
1223 par->itemdepth = par->previous()->itemdepth;
1225 for (int i = 0; i < 10; ++i) {
1226 par->setCounter(i, 0);
1228 par->params().appendix(par->params().startOfAppendix());
1233 /* Maybe we have to increment the enumeration depth.
1234 * BUT, enumeration in a footnote is considered in isolation from its
1235 * surrounding paragraph so don't increment if this is the
1236 * first line of the footnote
1237 * AND, bibliographies can't have their depth changed ie. they
1238 * are always of depth 0
1241 && par->previous()->getDepth() < par->getDepth()
1242 && textclasslist.Style(buf->params.textclass,
1243 par->previous()->getLayout()
1244 ).labeltype == LABEL_COUNTER_ENUMI
1245 && par->enumdepth < 3
1246 && layout.labeltype != LABEL_BIBLIO) {
1250 /* Maybe we have to decrement the enumeration depth, see note above */
1252 && par->previous()->getDepth() > par->getDepth()
1253 && layout.labeltype != LABEL_BIBLIO) {
1254 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1255 par->setCounter(6 + par->enumdepth,
1256 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1257 /* reset the counters.
1258 * A depth change is like a breaking layout
1260 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1261 par->setCounter(i, 0);
1264 if (!par->params().labelString().empty()) {
1265 par->params().labelString(string());
1268 if (layout.margintype == MARGIN_MANUAL) {
1269 if (par->params().labelWidthString().empty()) {
1270 par->setLabelWidthString(layout.labelstring());
1273 par->setLabelWidthString(string());
1276 /* is it a layout that has an automatic label ? */
1277 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1279 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1280 if (i >= 0 && i<= buf->params.secnumdepth) {
1281 par->incCounter(i); // increment the counter
1283 // Is there a label? Useful for Chapter layout
1284 if (!par->params().appendix()) {
1285 if (!layout.labelstring().empty())
1286 par->params().labelString(layout.labelstring());
1288 par->params().labelString(string());
1290 if (!layout.labelstring_appendix().empty())
1291 par->params().labelString(layout.labelstring_appendix());
1293 par->params().labelString(string());
1296 std::ostringstream s;
1298 if (!par->params().appendix()) {
1299 switch (2 * LABEL_COUNTER_CHAPTER -
1300 textclass.maxcounter() + i) {
1301 case LABEL_COUNTER_CHAPTER:
1302 s << par->getCounter(i);
1304 case LABEL_COUNTER_SECTION:
1305 s << par->getCounter(i - 1) << '.'
1306 << par->getCounter(i);
1308 case LABEL_COUNTER_SUBSECTION:
1309 s << par->getCounter(i - 2) << '.'
1310 << par->getCounter(i - 1) << '.'
1311 << par->getCounter(i);
1313 case LABEL_COUNTER_SUBSUBSECTION:
1314 s << par->getCounter(i - 3) << '.'
1315 << par->getCounter(i - 2) << '.'
1316 << par->getCounter(i - 1) << '.'
1317 << par->getCounter(i);
1320 case LABEL_COUNTER_PARAGRAPH:
1321 s << par->getCounter(i - 4) << '.'
1322 << par->getCounter(i - 3) << '.'
1323 << par->getCounter(i - 2) << '.'
1324 << par->getCounter(i - 1) << '.'
1325 << par->getCounter(i);
1327 case LABEL_COUNTER_SUBPARAGRAPH:
1328 s << par->getCounter(i - 5) << '.'
1329 << par->getCounter(i - 4) << '.'
1330 << par->getCounter(i - 3) << '.'
1331 << par->getCounter(i - 2) << '.'
1332 << par->getCounter(i - 1) << '.'
1333 << par->getCounter(i);
1337 // Can this ever be reached? And in the
1338 // case it is, how can this be correct?
1340 s << par->getCounter(i) << '.';
1343 } else { // appendix
1344 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1345 case LABEL_COUNTER_CHAPTER:
1346 if (par->isRightToLeftPar(buf->params))
1347 s << hebrewCounter(par->getCounter(i));
1349 s << alphaCounter(par->getCounter(i));
1351 case LABEL_COUNTER_SECTION:
1352 if (par->isRightToLeftPar(buf->params))
1353 s << hebrewCounter(par->getCounter(i - 1));
1355 s << alphaCounter(par->getCounter(i - 1));
1358 << par->getCounter(i);
1361 case LABEL_COUNTER_SUBSECTION:
1362 if (par->isRightToLeftPar(buf->params))
1363 s << hebrewCounter(par->getCounter(i - 2));
1365 s << alphaCounter(par->getCounter(i - 2));
1368 << par->getCounter(i-1) << '.'
1369 << par->getCounter(i);
1372 case LABEL_COUNTER_SUBSUBSECTION:
1373 if (par->isRightToLeftPar(buf->params))
1374 s << hebrewCounter(par->getCounter(i-3));
1376 s << alphaCounter(par->getCounter(i-3));
1379 << par->getCounter(i-2) << '.'
1380 << par->getCounter(i-1) << '.'
1381 << par->getCounter(i);
1384 case LABEL_COUNTER_PARAGRAPH:
1385 if (par->isRightToLeftPar(buf->params))
1386 s << hebrewCounter(par->getCounter(i-4));
1388 s << alphaCounter(par->getCounter(i-4));
1391 << par->getCounter(i-3) << '.'
1392 << par->getCounter(i-2) << '.'
1393 << par->getCounter(i-1) << '.'
1394 << par->getCounter(i);
1397 case LABEL_COUNTER_SUBPARAGRAPH:
1398 if (par->isRightToLeftPar(buf->params))
1399 s << hebrewCounter(par->getCounter(i-5));
1401 s << alphaCounter(par->getCounter(i-5));
1404 << par->getCounter(i-4) << '.'
1405 << par->getCounter(i-3) << '.'
1406 << par->getCounter(i-2) << '.'
1407 << par->getCounter(i-1) << '.'
1408 << par->getCounter(i);
1412 // Can this ever be reached? And in the
1413 // case it is, how can this be correct?
1415 s << par->getCounter(i) << '.';
1421 par->params().labelString(par->params().labelString() +s.str().c_str());
1422 // We really want to remove the c_str as soon as
1425 for (i++; i < 10; ++i) {
1426 // reset the following counters
1427 par->setCounter(i, 0);
1429 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1430 for (i++; i < 10; ++i) {
1431 // reset the following counters
1432 par->setCounter(i, 0);
1434 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1435 par->incCounter(i + par->enumdepth);
1436 int number = par->getCounter(i + par->enumdepth);
1438 std::ostringstream s;
1440 switch (par->enumdepth) {
1442 if (par->isRightToLeftPar(buf->params))
1444 << hebrewCounter(number)
1448 << loweralphaCounter(number)
1452 if (par->isRightToLeftPar(buf->params))
1453 s << '.' << romanCounter(number);
1455 s << romanCounter(number) << '.';
1458 if (par->isRightToLeftPar(buf->params))
1460 << alphaCounter(number);
1462 s << alphaCounter(number)
1466 if (par->isRightToLeftPar(buf->params))
1473 par->params().labelString(s.str().c_str());
1474 // we really want to get rid of that c_str()
1476 for (i += par->enumdepth + 1; i < 10; ++i)
1477 par->setCounter(i, 0); /* reset the following counters */
1480 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1481 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1483 int number = par->getCounter(i);
1485 InsetCommandParams p( "bibitem" );
1486 par->bibkey = new InsetBibKey(p);
1488 par->bibkey->setCounter(number);
1489 par->params().labelString(layout.labelstring());
1491 // In biblio should't be following counters but...
1493 string s = layout.labelstring();
1495 // the caption hack:
1496 if (layout.labeltype == LABEL_SENSITIVE) {
1497 bool isOK (par->InInset() && par->InInset()->owner() &&
1498 (par->InInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1501 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1503 = floatList.getType(tmp->type());
1504 // We should get the correct number here too.
1505 s = fl.name() + " #:";
1507 /* par->SetLayout(0);
1508 s = layout->labelstring; */
1509 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1510 ? " :úåòîùî øñç" : "Senseless: ";
1513 par->params().labelString(s);
1515 /* reset the enumeration counter. They are always resetted
1516 * when there is any other layout between */
1517 for (int i = 6 + par->enumdepth; i < 10; ++i)
1518 par->setCounter(i, 0);
1523 /* Updates all counters BEHIND the row. Changed paragraphs
1524 * with a dynamic left margin will be rebroken. */
1525 void LyXText::updateCounters(BufferView * bview, Row * row) const
1533 par = row->par()->next();
1537 while (row->par() != par)
1540 setCounter(bview->buffer(), par);
1542 /* now check for the headline layouts. remember that they
1543 * have a dynamic left margin */
1544 if ((textclasslist.Style(bview->buffer()->params.textclass,
1545 par->layout).margintype == MARGIN_DYNAMIC
1546 || textclasslist.Style(bview->buffer()->params.textclass,
1547 par->layout).labeltype == LABEL_SENSITIVE)) {
1549 /* Rebreak the paragraph */
1550 removeParagraph(row);
1551 appendParagraph(bview, row);
1558 /* insets an inset. */
1559 void LyXText::insertInset(BufferView * bview, Inset * inset)
1561 if (!cursor.par()->insertInsetAllowed(inset))
1563 setUndo(bview, Undo::INSERT,
1564 cursor.par(), cursor.par()->next());
1565 cursor.par()->insertInset(cursor.pos(), inset);
1566 insertChar(bview, Paragraph::META_INSET); /* just to rebreak and refresh correctly.
1567 * The character will not be inserted a
1570 // If we enter a highly editable inset the cursor should be to before
1571 // the inset. This couldn't happen before as Undo was not handled inside
1572 // inset now after the Undo LyX tries to call inset->Edit(...) again
1573 // and cannot do this as the cursor is behind the inset and GetInset
1574 // does not return the inset!
1575 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1576 cursorLeft(bview, true);
1582 void LyXText::copyEnvironmentType()
1584 copylayouttype = cursor.par()->getLayout();
1588 void LyXText::pasteEnvironmentType(BufferView * bview)
1590 setLayout(bview, copylayouttype);
1594 void LyXText::cutSelection(BufferView * bview, bool doclear)
1596 // Stuff what we got on the clipboard. Even if there is no selection.
1598 // There is a problem with having the stuffing here in that the
1599 // larger the selection the slower LyX will get. This can be
1600 // solved by running the line below only when the selection has
1601 // finished. The solution used currently just works, to make it
1602 // faster we need to be more clever and probably also have more
1603 // calls to stuffClipboard. (Lgb)
1604 bview->stuffClipboard(selectionAsString(bview->buffer()));
1606 // This doesn't make sense, if there is no selection
1607 if (!selection.set())
1610 // OK, we have a selection. This is always between selection.start
1611 // and selection.end
1613 // make sure that the depth behind the selection are restored, too
1614 Paragraph * endpar = selection.end.par()->next();
1615 Paragraph * undoendpar = endpar;
1617 if (endpar && endpar->getDepth()) {
1618 while (endpar && endpar->getDepth()) {
1619 endpar = endpar->next();
1620 undoendpar = endpar;
1622 } else if (endpar) {
1623 endpar = endpar->next(); // because of parindents etc.
1626 setUndo(bview, Undo::DELETE,
1627 selection.start.par(), undoendpar);
1629 // there are two cases: cut only within one paragraph or
1630 // more than one paragraph
1631 if (selection.start.par() == selection.end.par()) {
1632 // only within one paragraph
1633 endpar = selection.end.par();
1634 int pos = selection.end.pos();
1635 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1636 selection.start.pos(), pos,
1637 bview->buffer()->params.textclass, doclear);
1638 selection.end.pos(pos);
1640 endpar = selection.end.par();
1641 int pos = selection.end.pos();
1642 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1643 selection.start.pos(), pos,
1644 bview->buffer()->params.textclass, doclear);
1646 selection.end.par(endpar);
1647 selection.end.pos(pos);
1648 cursor.pos(selection.end.pos());
1650 endpar = endpar->next();
1652 // sometimes necessary
1654 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1656 redoParagraphs(bview, selection.start, endpar);
1658 // cutSelection can invalidate the cursor so we need to set
1660 cursor = selection.start;
1662 // need a valid cursor. (Lgb)
1663 clearSelection(bview);
1665 setCursor(bview, cursor.par(), cursor.pos());
1666 selection.cursor = cursor;
1667 updateCounters(bview, cursor.row());
1671 void LyXText::copySelection(BufferView * bview)
1673 // Stuff what we got on the clipboard. Even if there is no selection.
1675 // There is a problem with having the stuffing here in that the
1676 // larger the selection the slower LyX will get. This can be
1677 // solved by running the line below only when the selection has
1678 // finished. The solution used currently just works, to make it
1679 // faster we need to be more clever and probably also have more
1680 // calls to stuffClipboard. (Lgb)
1681 bview->stuffClipboard(selectionAsString(bview->buffer()));
1683 // this doesnt make sense, if there is no selection
1684 if (!selection.set())
1687 // ok we have a selection. This is always between selection.start
1688 // and sel_end cursor
1690 // copy behind a space if there is one
1691 while (selection.start.par()->size() > selection.start.pos()
1692 && selection.start.par()->isLineSeparator(selection.start.pos())
1693 && (selection.start.par() != selection.end.par()
1694 || selection.start.pos() < selection.end.pos()))
1695 selection.start.pos(selection.start.pos() + 1);
1697 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1698 selection.start.pos(), selection.end.pos(),
1699 bview->buffer()->params.textclass);
1703 void LyXText::pasteSelection(BufferView * bview)
1705 // this does not make sense, if there is nothing to paste
1706 if (!CutAndPaste::checkPastePossible(cursor.par()))
1709 setUndo(bview, Undo::INSERT,
1710 cursor.par(), cursor.par()->next());
1713 Paragraph * actpar = cursor.par();
1715 int pos = cursor.pos();
1716 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1717 bview->buffer()->params.textclass);
1719 redoParagraphs(bview, cursor, endpar);
1721 setCursor(bview, cursor.par(), cursor.pos());
1722 clearSelection(bview);
1724 selection.cursor = cursor;
1725 setCursor(bview, actpar, pos);
1726 setSelection(bview);
1727 updateCounters(bview, cursor.row());
1731 // returns a pointer to the very first Paragraph
1732 Paragraph * LyXText::firstParagraph() const
1734 return ownerParagraph();
1738 // sets the selection over the number of characters of string, no check!!
1739 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1744 selection.cursor = cursor;
1745 for (string::size_type i = 0; i < str.length(); ++i)
1747 setSelection(bview);
1751 // simple replacing. The font of the first selected character is used
1752 void LyXText::replaceSelectionWithString(BufferView * bview,
1755 setCursorParUndo(bview);
1758 if (!selection.set()) { // create a dummy selection
1759 selection.end = cursor;
1760 selection.start = cursor;
1763 // Get font setting before we cut
1764 Paragraph::size_type pos = selection.end.pos();
1765 LyXFont const font = selection.start.par()
1766 ->getFontSettings(bview->buffer()->params,
1767 selection.start.pos());
1769 // Insert the new string
1770 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1771 selection.end.par()->insertChar(pos, (*cit), font);
1775 // Cut the selection
1776 cutSelection(bview);
1782 // needed to insert the selection
1783 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1785 Paragraph * par = cursor.par();
1786 Paragraph::size_type pos = cursor.pos();
1787 Paragraph * endpar = cursor.par()->next();
1789 setCursorParUndo(bview);
1791 bool isEnvironment =
1792 textclasslist.Style(bview->buffer()->params.textclass,
1793 cursor.par()->getLayout()).isEnvironment();
1795 textclasslist.Style(bview->buffer()->params.textclass,
1796 cursor.par()->getLayout()).free_spacing;
1798 textclasslist.Style(bview->buffer()->params.textclass,
1799 cursor.par()->getLayout()).keepempty;
1801 // only to be sure, should not be neccessary
1802 clearSelection(bview);
1804 // insert the string, don't insert doublespace
1805 bool space_inserted = true;
1806 for(string::const_iterator cit = str.begin();
1807 cit != str.end(); ++cit) {
1809 if (par->size() || keepempty) {
1810 par->breakParagraph(bview->buffer()->params,
1811 pos, isEnvironment);
1814 space_inserted = true;
1818 // do not insert consecutive spaces if !free_spacing
1819 } else if ((*cit == ' ' || *cit == '\t')
1820 && space_inserted && !free_spacing) {
1822 } else if (*cit == '\t') {
1823 if (!free_spacing) {
1824 // tabs are like spaces here
1825 par->insertChar(pos, ' ',
1828 space_inserted = true;
1830 const Paragraph::value_type nb = 8 - pos % 8;
1831 for (Paragraph::size_type a = 0;
1833 par->insertChar(pos, ' ',
1837 space_inserted = true;
1839 } else if (!IsPrintable(*cit)) {
1840 // Ignore unprintables
1843 // just insert the character
1844 par->insertChar(pos, *cit, current_font);
1846 space_inserted = (*cit == ' ');
1851 redoParagraphs(bview, cursor, endpar);
1852 setCursor(bview, cursor.par(), cursor.pos());
1853 selection.cursor = cursor;
1854 setCursor(bview, par, pos);
1855 setSelection(bview);
1859 /* turns double-CR to single CR, others where converted into one
1860 blank. Then InsertStringAsLines is called */
1861 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1863 string linestr(str);
1864 bool newline_inserted = false;
1865 for (string::size_type i = 0; i < linestr.length(); ++i) {
1866 if (linestr[i] == '\n') {
1867 if (newline_inserted) {
1868 // we know that \r will be ignored by
1869 // InsertStringA. Of course, it is a dirty
1870 // trick, but it works...
1871 linestr[i - 1] = '\r';
1875 newline_inserted = true;
1877 } else if (IsPrintable(linestr[i])) {
1878 newline_inserted = false;
1881 insertStringAsLines(bview, linestr);
1885 bool LyXText::gotoNextInset(BufferView * bview,
1886 std::vector<Inset::Code> const & codes,
1887 string const & contents) const
1889 LyXCursor res = cursor;
1892 if (res.pos() < res.par()->size() - 1) {
1893 res.pos(res.pos() + 1);
1895 res.par(res.par()->next());
1899 } while (res.par() &&
1900 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1901 && (inset = res.par()->getInset(res.pos())) != 0
1902 && find(codes.begin(), codes.end(), inset->lyxCode())
1904 && (contents.empty() ||
1905 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1909 setCursor(bview, res.par(), res.pos());
1916 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1917 Paragraph::size_type pos)
1919 LyXCursor tmpcursor;
1922 Paragraph::size_type z;
1923 Row * row = getRow(par, pos, y);
1925 // is there a break one row above
1926 if (row->previous() && row->previous()->par() == row->par()) {
1927 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1928 if (z >= row->pos()) {
1929 // set the dimensions of the row above
1930 y -= row->previous()->height();
1932 refresh_row = row->previous();
1933 status(bview, LyXText::NEED_MORE_REFRESH);
1935 breakAgain(bview, row->previous());
1937 // set the cursor again. Otherwise
1938 // dangling pointers are possible
1939 setCursor(bview, cursor.par(), cursor.pos(),
1940 false, cursor.boundary());
1941 selection.cursor = cursor;
1946 int const tmpheight = row->height();
1947 Paragraph::size_type const tmplast = rowLast(row);
1951 breakAgain(bview, row);
1952 if (row->height() == tmpheight && rowLast(row) == tmplast)
1953 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1955 status(bview, LyXText::NEED_MORE_REFRESH);
1957 // check the special right address boxes
1958 if (textclasslist.Style(bview->buffer()->params.textclass,
1959 par->getLayout()).margintype
1960 == MARGIN_RIGHT_ADDRESS_BOX) {
1967 redoDrawingOfParagraph(bview, tmpcursor);
1970 // set the cursor again. Otherwise dangling pointers are possible
1971 // also set the selection
1973 if (selection.set()) {
1975 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1976 false, selection.cursor.boundary());
1977 selection.cursor = cursor;
1978 setCursorIntern(bview, selection.start.par(),
1979 selection.start.pos(),
1980 false, selection.start.boundary());
1981 selection.start = cursor;
1982 setCursorIntern(bview, selection.end.par(),
1983 selection.end.pos(),
1984 false, selection.end.boundary());
1985 selection.end = cursor;
1986 setCursorIntern(bview, last_sel_cursor.par(),
1987 last_sel_cursor.pos(),
1988 false, last_sel_cursor.boundary());
1989 last_sel_cursor = cursor;
1992 setCursorIntern(bview, cursor.par(), cursor.pos(),
1993 false, cursor.boundary());
1997 // returns false if inset wasn't found
1998 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2000 // first check the current paragraph
2001 int pos = cursor.par()->getPositionOfInset(inset);
2003 checkParagraph(bview, cursor.par(), pos);
2007 // check every paragraph
2009 Paragraph * par = firstParagraph();
2011 pos = par->getPositionOfInset(inset);
2013 checkParagraph(bview, par, pos);
2023 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2024 Paragraph::size_type pos,
2025 bool setfont, bool boundary) const
2027 LyXCursor old_cursor = cursor;
2028 setCursorIntern(bview, par, pos, setfont, boundary);
2029 deleteEmptyParagraphMechanism(bview, old_cursor);
2033 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2034 Paragraph::size_type pos, bool boundary) const
2038 cur.boundary(boundary);
2040 /* get the cursor y position in text */
2042 Row * row = getRow(par, pos, y);
2043 /* y is now the beginning of the cursor row */
2044 y += row->baseline();
2045 /* y is now the cursor baseline */
2048 /* now get the cursors x position */
2050 float fill_separator;
2052 float fill_label_hfill;
2053 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2055 Paragraph::size_type cursor_vpos = 0;
2056 Paragraph::size_type last = rowLastPrintable(row);
2058 if (pos > last + 1) // This shouldn't happen.
2060 else if (pos < row->pos())
2063 if (last < row->pos())
2064 cursor_vpos = row->pos();
2065 else if (pos > last && !boundary)
2066 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2067 ? row->pos() : last + 1;
2068 else if (pos > row->pos() &&
2069 (pos > last || boundary))
2070 /// Place cursor after char at (logical) position pos - 1
2071 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2072 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2074 /// Place cursor before char at (logical) position pos
2075 cursor_vpos = (bidi_level(pos) % 2 == 0)
2076 ? log2vis(pos) : log2vis(pos) + 1;
2078 Paragraph::size_type main_body =
2079 beginningOfMainBody(bview->buffer(), row->par());
2080 if ((main_body > 0) &&
2081 ((main_body-1 > last) ||
2082 !row->par()->isLineSeparator(main_body-1)))
2085 for (Paragraph::size_type vpos = row->pos();
2086 vpos < cursor_vpos; ++vpos) {
2087 pos = vis2log(vpos);
2088 if (main_body > 0 && pos == main_body - 1) {
2089 x += fill_label_hfill +
2090 lyxfont::width(textclasslist.Style(
2091 bview->buffer()->params.textclass,
2092 row->par()->getLayout())
2094 getFont(bview->buffer(), row->par(), -2));
2095 if (row->par()->isLineSeparator(main_body-1))
2096 x -= singleWidth(bview, row->par(),main_body-1);
2098 if (hfillExpansion(bview->buffer(), row, pos)) {
2099 x += singleWidth(bview, row->par(), pos);
2100 if (pos >= main_body)
2103 x += fill_label_hfill;
2104 } else if (row->par()->isSeparator(pos)) {
2105 x += singleWidth(bview, row->par(), pos);
2106 if (pos >= main_body)
2107 x += fill_separator;
2109 x += singleWidth(bview, row->par(), pos);
2118 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2119 Paragraph::size_type pos,
2120 bool setfont, bool boundary) const
2122 InsetText * it = static_cast<InsetText *>(par->InInset());
2123 if (it && (it != inset_owner)) {
2124 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2127 setCursor(bview, cursor, par, pos, boundary);
2129 setCurrentFont(bview);
2134 void LyXText::setCurrentFont(BufferView * bview) const
2136 Paragraph::size_type pos = cursor.pos();
2137 if (cursor.boundary() && pos > 0)
2141 if (pos == cursor.par()->size())
2143 else // potentional bug... BUG (Lgb)
2144 if (cursor.par()->isSeparator(pos)) {
2145 if (pos > cursor.row()->pos() &&
2146 bidi_level(pos) % 2 ==
2147 bidi_level(pos - 1) % 2)
2149 else if (pos + 1 < cursor.par()->size())
2155 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2156 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2158 if (cursor.pos() == cursor.par()->size() &&
2159 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2160 !cursor.boundary()) {
2161 Language const * lang =
2162 cursor.par()->getParLanguage(bview->buffer()->params);
2163 current_font.setLanguage(lang);
2164 current_font.setNumber(LyXFont::OFF);
2165 real_current_font.setLanguage(lang);
2166 real_current_font.setNumber(LyXFont::OFF);
2171 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2173 LyXCursor old_cursor = cursor;
2175 /* get the row first */
2177 Row * row = getRowNearY(y);
2178 cursor.par(row->par());
2181 int column = getColumnNearX(bview, row, x, bound);
2182 cursor.pos(row->pos() + column);
2184 cursor.y(y + row->baseline());
2186 cursor.boundary(bound);
2187 setCurrentFont(bview);
2188 deleteEmptyParagraphMechanism(bview, old_cursor);
2192 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2195 /* get the row first */
2197 Row * row = getRowNearY(y);
2199 int column = getColumnNearX(bview, row, x, bound);
2201 cur.par(row->par());
2202 cur.pos(row->pos() + column);
2204 cur.y(y + row->baseline());
2206 cur.boundary(bound);
2210 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2212 if (cursor.pos() > 0) {
2213 bool boundary = cursor.boundary();
2214 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2215 if (!internal && !boundary &&
2216 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2217 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2218 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2219 Paragraph * par = cursor.par()->previous();
2220 setCursor(bview, par, par->size());
2225 void LyXText::cursorRight(BufferView * bview, bool internal) const
2227 if (!internal && cursor.boundary() &&
2228 !cursor.par()->isNewline(cursor.pos()))
2229 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2230 else if (cursor.pos() < cursor.par()->size()) {
2231 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2233 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2234 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2235 } else if (cursor.par()->next())
2236 setCursor(bview, cursor.par()->next(), 0);
2240 void LyXText::cursorUp(BufferView * bview) const
2242 setCursorFromCoordinates(bview, cursor.x_fix(),
2243 cursor.y() - cursor.row()->baseline() - 1);
2247 void LyXText::cursorDown(BufferView * bview) const
2249 setCursorFromCoordinates(bview, cursor.x_fix(),
2250 cursor.y() - cursor.row()->baseline()
2251 + cursor.row()->height() + 1);
2255 void LyXText::cursorUpParagraph(BufferView * bview) const
2257 if (cursor.pos() > 0) {
2258 setCursor(bview, cursor.par(), 0);
2260 else if (cursor.par()->previous()) {
2261 setCursor(bview, cursor.par()->previous(), 0);
2266 void LyXText::cursorDownParagraph(BufferView * bview) const
2268 if (cursor.par()->next()) {
2269 setCursor(bview, cursor.par()->next(), 0);
2271 setCursor(bview, cursor.par(), cursor.par()->size());
2276 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2277 LyXCursor const & old_cursor) const
2279 // Would be wrong to delete anything if we have a selection.
2280 if (selection.set()) return;
2282 // We allow all kinds of "mumbo-jumbo" when freespacing.
2283 if (textclasslist.Style(bview->buffer()->params.textclass,
2284 old_cursor.par()->getLayout()).free_spacing)
2287 bool deleted = false;
2289 /* Ok I'll put some comments here about what is missing.
2290 I have fixed BackSpace (and thus Delete) to not delete
2291 double-spaces automagically. I have also changed Cut,
2292 Copy and Paste to hopefully do some sensible things.
2293 There are still some small problems that can lead to
2294 double spaces stored in the document file or space at
2295 the beginning of paragraphs. This happens if you have
2296 the cursor betwenn to spaces and then save. Or if you
2297 cut and paste and the selection have a space at the
2298 beginning and then save right after the paste. I am
2299 sure none of these are very hard to fix, but I will
2300 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2301 that I can get some feedback. (Lgb)
2304 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2305 // delete the LineSeparator.
2308 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2309 // delete the LineSeparator.
2312 // If the pos around the old_cursor were spaces, delete one of them.
2313 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2314 // Only if the cursor has really moved
2316 if (old_cursor.pos() > 0
2317 && old_cursor.pos() < old_cursor.par()->size()
2318 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2319 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2320 old_cursor.par()->erase(old_cursor.pos() - 1);
2321 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2323 if (old_cursor.par() == cursor.par() &&
2324 cursor.pos() > old_cursor.pos()) {
2325 setCursorIntern(bview, cursor.par(),
2328 setCursorIntern(bview, cursor.par(),
2334 // Do not delete empty paragraphs with keepempty set.
2335 if ((textclasslist.Style(bview->buffer()->params.textclass,
2336 old_cursor.par()->getLayout())).keepempty)
2339 LyXCursor tmpcursor;
2341 if (old_cursor.par() != cursor.par()) {
2342 if ((old_cursor.par()->size() == 0
2343 || (old_cursor.par()->size() == 1
2344 && old_cursor.par()->isLineSeparator(0)))) {
2345 // ok, we will delete anything
2347 // make sure that you do not delete any environments
2348 status(bview, LyXText::NEED_MORE_REFRESH);
2351 if (old_cursor.row()->previous()) {
2352 refresh_row = old_cursor.row()->previous();
2353 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2355 cursor = old_cursor; // that undo can restore the right cursor position
2356 Paragraph * endpar = old_cursor.par()->next();
2357 if (endpar && endpar->getDepth()) {
2358 while (endpar && endpar->getDepth()) {
2359 endpar = endpar->next();
2362 setUndo(bview, Undo::DELETE,
2368 removeRow(old_cursor.row());
2369 if (ownerParagraph() == old_cursor.par()) {
2370 ownerParagraph(ownerParagraph()->next());
2373 delete old_cursor.par();
2375 /* Breakagain the next par. Needed
2376 * because of the parindent that
2377 * can occur or dissappear. The
2378 * next row can change its height,
2379 * if there is another layout before */
2380 if (refresh_row->next()) {
2381 breakAgain(bview, refresh_row->next());
2382 updateCounters(bview, refresh_row);
2384 setHeightOfRow(bview, refresh_row);
2386 refresh_row = old_cursor.row()->next();
2387 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2390 cursor = old_cursor; // that undo can restore the right cursor position
2391 Paragraph * endpar = old_cursor.par()->next();
2392 if (endpar && endpar->getDepth()) {
2393 while (endpar && endpar->getDepth()) {
2394 endpar = endpar->next();
2397 setUndo(bview, Undo::DELETE,
2403 removeRow(old_cursor.row());
2405 if (ownerParagraph() == old_cursor.par()) {
2406 ownerParagraph(ownerParagraph()->next());
2409 delete old_cursor.par();
2411 /* Breakagain the next par. Needed
2412 because of the parindent that can
2413 occur or dissappear.
2414 The next row can change its height,
2415 if there is another layout before
2418 breakAgain(bview, refresh_row);
2419 updateCounters(bview, refresh_row->previous());
2425 setCursorIntern(bview, cursor.par(), cursor.pos());
2427 if (selection.cursor.par() == old_cursor.par()
2428 && selection.cursor.pos() == selection.cursor.pos()) {
2429 // correct selection
2430 selection.cursor = cursor;
2434 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2435 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2437 setCursorIntern(bview, cursor.par(), cursor.pos());
2438 selection.cursor = cursor;
2445 Paragraph * LyXText::getParFromID(int id) const
2449 Paragraph * result = firstParagraph();
2450 Paragraph * ires = 0;
2451 while (result && result->id() != id) {
2452 if ((ires = result->getParFromID(id)))
2454 result = result->next();
2459 void LyXText::toggleAppendix(BufferView * bview)
2461 Paragraph * par = cursor.par();
2462 bool start = !par->params().startOfAppendix();
2464 // ensure that we have only one start_of_appendix in this document
2465 Paragraph * tmp = firstParagraph();
2466 for (; tmp; tmp = tmp->next())
2467 tmp->params().startOfAppendix(false);
2469 par->params().startOfAppendix(start);
2471 // we can set the refreshing parameters now
2472 status(bview, LyXText::NEED_MORE_REFRESH);
2474 refresh_row = 0; // not needed for full update
2475 updateCounters(bview, 0);
2476 setCursor(bview, cursor.par(), cursor.pos());
2480 Paragraph * LyXText::ownerParagraph() const
2483 return inset_owner->paragraph();
2485 return bv_owner->buffer()->paragraph;
2489 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2492 inset_owner->paragraph(p);
2494 bv_owner->buffer()->paragraph = p;
2498 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2500 Paragraph * op = getParFromID(id);
2501 if (op && op->InInset()) {
2502 static_cast<InsetText *>(op->InInset())->paragraph(p);
2505 inset_owner->paragraph(p);
2507 bv_owner->buffer()->paragraph = p;
2513 LyXText::text_status LyXText::status() const
2519 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2521 if ((status_ != NEED_MORE_REFRESH) ||
2522 (status_ == NEED_MORE_REFRESH) && (st != NEED_VERY_LITTLE_REFRESH))
2525 if (inset_owner && st != UNCHANGED)
2526 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);