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 first paragraph
1206 if (par->previous()) {
1207 for (int i = 0; i < 10; ++i) {
1208 par->setCounter(i, par->previous()->getFirstCounter(i));
1210 par->params().appendix(par->previous()->params().appendix());
1211 if (!par->params().appendix() && par->params().startOfAppendix()) {
1212 par->params().appendix(true);
1213 for (int i = 0; i < 10; ++i) {
1214 par->setCounter(i, 0);
1217 par->enumdepth = par->previous()->enumdepth;
1218 par->itemdepth = par->previous()->itemdepth;
1220 for (int i = 0; i < 10; ++i) {
1221 par->setCounter(i, 0);
1223 par->params().appendix(par->params().startOfAppendix());
1228 /* Maybe we have to increment the enumeration depth.
1229 * BUT, enumeration in a footnote is considered in isolation from its
1230 * surrounding paragraph so don't increment if this is the
1231 * first line of the footnote
1232 * AND, bibliographies can't have their depth changed ie. they
1233 * are always of depth 0
1236 && par->previous()->getDepth() < par->getDepth()
1237 && textclasslist.Style(buf->params.textclass,
1238 par->previous()->getLayout()
1239 ).labeltype == LABEL_COUNTER_ENUMI
1240 && par->enumdepth < 3
1241 && layout.labeltype != LABEL_BIBLIO) {
1245 /* Maybe we have to decrement the enumeration depth, see note above */
1247 && par->previous()->getDepth() > par->getDepth()
1248 && layout.labeltype != LABEL_BIBLIO) {
1249 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1250 par->setCounter(6 + par->enumdepth,
1251 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1252 /* reset the counters.
1253 * A depth change is like a breaking layout
1255 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1256 par->setCounter(i, 0);
1259 if (!par->params().labelString().empty()) {
1260 par->params().labelString(string());
1263 if (layout.margintype == MARGIN_MANUAL) {
1264 if (par->params().labelWidthString().empty()) {
1265 par->setLabelWidthString(layout.labelstring());
1268 par->setLabelWidthString(string());
1271 /* is it a layout that has an automatic label ? */
1272 if (layout.labeltype >= LABEL_COUNTER_CHAPTER) {
1274 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1275 if (i >= 0 && i<= buf->params.secnumdepth) {
1276 par->incCounter(i); // increment the counter
1278 // Is there a label? Useful for Chapter layout
1279 if (!par->params().appendix()) {
1280 if (!layout.labelstring().empty())
1281 par->params().labelString(layout.labelstring());
1283 par->params().labelString(string());
1285 if (!layout.labelstring_appendix().empty())
1286 par->params().labelString(layout.labelstring_appendix());
1288 par->params().labelString(string());
1291 std::ostringstream s;
1293 if (!par->params().appendix()) {
1294 switch (2 * LABEL_COUNTER_CHAPTER -
1295 textclass.maxcounter() + i) {
1296 case LABEL_COUNTER_CHAPTER:
1297 s << par->getCounter(i);
1299 case LABEL_COUNTER_SECTION:
1300 s << par->getCounter(i - 1) << '.'
1301 << par->getCounter(i);
1303 case LABEL_COUNTER_SUBSECTION:
1304 s << par->getCounter(i - 2) << '.'
1305 << par->getCounter(i - 1) << '.'
1306 << par->getCounter(i);
1308 case LABEL_COUNTER_SUBSUBSECTION:
1309 s << par->getCounter(i - 3) << '.'
1310 << par->getCounter(i - 2) << '.'
1311 << par->getCounter(i - 1) << '.'
1312 << par->getCounter(i);
1315 case LABEL_COUNTER_PARAGRAPH:
1316 s << par->getCounter(i - 4) << '.'
1317 << par->getCounter(i - 3) << '.'
1318 << par->getCounter(i - 2) << '.'
1319 << par->getCounter(i - 1) << '.'
1320 << par->getCounter(i);
1322 case LABEL_COUNTER_SUBPARAGRAPH:
1323 s << par->getCounter(i - 5) << '.'
1324 << par->getCounter(i - 4) << '.'
1325 << par->getCounter(i - 3) << '.'
1326 << par->getCounter(i - 2) << '.'
1327 << par->getCounter(i - 1) << '.'
1328 << par->getCounter(i);
1332 // Can this ever be reached? And in the
1333 // case it is, how can this be correct?
1335 s << par->getCounter(i) << '.';
1338 } else { // appendix
1339 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1340 case LABEL_COUNTER_CHAPTER:
1341 if (par->isRightToLeftPar(buf->params))
1342 s << hebrewCounter(par->getCounter(i));
1344 s << alphaCounter(par->getCounter(i));
1346 case LABEL_COUNTER_SECTION:
1347 if (par->isRightToLeftPar(buf->params))
1348 s << hebrewCounter(par->getCounter(i - 1));
1350 s << alphaCounter(par->getCounter(i - 1));
1353 << par->getCounter(i);
1356 case LABEL_COUNTER_SUBSECTION:
1357 if (par->isRightToLeftPar(buf->params))
1358 s << hebrewCounter(par->getCounter(i - 2));
1360 s << alphaCounter(par->getCounter(i - 2));
1363 << par->getCounter(i-1) << '.'
1364 << par->getCounter(i);
1367 case LABEL_COUNTER_SUBSUBSECTION:
1368 if (par->isRightToLeftPar(buf->params))
1369 s << hebrewCounter(par->getCounter(i-3));
1371 s << alphaCounter(par->getCounter(i-3));
1374 << par->getCounter(i-2) << '.'
1375 << par->getCounter(i-1) << '.'
1376 << par->getCounter(i);
1379 case LABEL_COUNTER_PARAGRAPH:
1380 if (par->isRightToLeftPar(buf->params))
1381 s << hebrewCounter(par->getCounter(i-4));
1383 s << alphaCounter(par->getCounter(i-4));
1386 << par->getCounter(i-3) << '.'
1387 << par->getCounter(i-2) << '.'
1388 << par->getCounter(i-1) << '.'
1389 << par->getCounter(i);
1392 case LABEL_COUNTER_SUBPARAGRAPH:
1393 if (par->isRightToLeftPar(buf->params))
1394 s << hebrewCounter(par->getCounter(i-5));
1396 s << alphaCounter(par->getCounter(i-5));
1399 << par->getCounter(i-4) << '.'
1400 << par->getCounter(i-3) << '.'
1401 << par->getCounter(i-2) << '.'
1402 << par->getCounter(i-1) << '.'
1403 << par->getCounter(i);
1407 // Can this ever be reached? And in the
1408 // case it is, how can this be correct?
1410 s << par->getCounter(i) << '.';
1416 par->params().labelString(par->params().labelString() +s.str().c_str());
1417 // We really want to remove the c_str as soon as
1420 for (i++; i < 10; ++i) {
1421 // reset the following counters
1422 par->setCounter(i, 0);
1424 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
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 par->incCounter(i + par->enumdepth);
1431 int number = par->getCounter(i + par->enumdepth);
1433 std::ostringstream s;
1435 switch (par->enumdepth) {
1437 if (par->isRightToLeftPar(buf->params))
1439 << hebrewCounter(number)
1443 << loweralphaCounter(number)
1447 if (par->isRightToLeftPar(buf->params))
1448 s << '.' << romanCounter(number);
1450 s << romanCounter(number) << '.';
1453 if (par->isRightToLeftPar(buf->params))
1455 << alphaCounter(number);
1457 s << alphaCounter(number)
1461 if (par->isRightToLeftPar(buf->params))
1468 par->params().labelString(s.str().c_str());
1469 // we really want to get rid of that c_str()
1471 for (i += par->enumdepth + 1; i < 10; ++i)
1472 par->setCounter(i, 0); /* reset the following counters */
1475 } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1476 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1478 int number = par->getCounter(i);
1480 InsetCommandParams p( "bibitem" );
1481 par->bibkey = new InsetBibKey(p);
1483 par->bibkey->setCounter(number);
1484 par->params().labelString(layout.labelstring());
1486 // In biblio should't be following counters but...
1488 string s = layout.labelstring();
1490 // the caption hack:
1491 if (layout.labeltype == LABEL_SENSITIVE) {
1492 bool isOK (par->InInset() && par->InInset()->owner() &&
1493 (par->InInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1496 InsetFloat * tmp = static_cast<InsetFloat*>(par->InInset()->owner());
1498 = floatList.getType(tmp->type());
1499 // We should get the correct number here too.
1500 s = fl.name() + " #:";
1502 /* par->SetLayout(0);
1503 s = layout->labelstring; */
1504 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1505 ? " :úåòîùî øñç" : "Senseless: ";
1508 par->params().labelString(s);
1510 /* reset the enumeration counter. They are always resetted
1511 * when there is any other layout between */
1512 for (int i = 6 + par->enumdepth; i < 10; ++i)
1513 par->setCounter(i, 0);
1518 /* Updates all counters BEHIND the row. Changed paragraphs
1519 * with a dynamic left margin will be rebroken. */
1520 void LyXText::updateCounters(BufferView * bview, Row * row) const
1528 par = row->par()->next();
1532 while (row->par() != par)
1535 setCounter(bview->buffer(), par);
1537 /* now check for the headline layouts. remember that they
1538 * have a dynamic left margin */
1539 if ((textclasslist.Style(bview->buffer()->params.textclass,
1540 par->layout).margintype == MARGIN_DYNAMIC
1541 || textclasslist.Style(bview->buffer()->params.textclass,
1542 par->layout).labeltype == LABEL_SENSITIVE)) {
1544 /* Rebreak the paragraph */
1545 removeParagraph(row);
1546 appendParagraph(bview, row);
1553 /* insets an inset. */
1554 void LyXText::insertInset(BufferView * bview, Inset * inset)
1556 if (!cursor.par()->insertInsetAllowed(inset))
1558 setUndo(bview, Undo::INSERT,
1559 cursor.par(), cursor.par()->next());
1560 cursor.par()->insertInset(cursor.pos(), inset);
1561 insertChar(bview, Paragraph::META_INSET); /* just to rebreak and refresh correctly.
1562 * The character will not be inserted a
1565 // If we enter a highly editable inset the cursor should be to before
1566 // the inset. This couldn't happen before as Undo was not handled inside
1567 // inset now after the Undo LyX tries to call inset->Edit(...) again
1568 // and cannot do this as the cursor is behind the inset and GetInset
1569 // does not return the inset!
1570 if (inset->editable() == Inset::HIGHLY_EDITABLE) {
1571 cursorLeft(bview, true);
1577 void LyXText::copyEnvironmentType()
1579 copylayouttype = cursor.par()->getLayout();
1583 void LyXText::pasteEnvironmentType(BufferView * bview)
1585 setLayout(bview, copylayouttype);
1589 void LyXText::cutSelection(BufferView * bview, bool doclear)
1591 // Stuff what we got on the clipboard. Even if there is no selection.
1593 // There is a problem with having the stuffing here in that the
1594 // larger the selection the slower LyX will get. This can be
1595 // solved by running the line below only when the selection has
1596 // finished. The solution used currently just works, to make it
1597 // faster we need to be more clever and probably also have more
1598 // calls to stuffClipboard. (Lgb)
1599 bview->stuffClipboard(selectionAsString(bview->buffer()));
1601 // This doesn't make sense, if there is no selection
1602 if (!selection.set())
1605 // OK, we have a selection. This is always between selection.start
1606 // and selection.end
1608 // make sure that the depth behind the selection are restored, too
1609 Paragraph * endpar = selection.end.par()->next();
1610 Paragraph * undoendpar = endpar;
1612 if (endpar && endpar->getDepth()) {
1613 while (endpar && endpar->getDepth()) {
1614 endpar = endpar->next();
1615 undoendpar = endpar;
1617 } else if (endpar) {
1618 endpar = endpar->next(); // because of parindents etc.
1621 setUndo(bview, Undo::DELETE,
1622 selection.start.par(), undoendpar);
1624 // there are two cases: cut only within one paragraph or
1625 // more than one paragraph
1626 if (selection.start.par() == selection.end.par()) {
1627 // only within one paragraph
1628 endpar = selection.end.par();
1629 int pos = selection.end.pos();
1630 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1631 selection.start.pos(), pos,
1632 bview->buffer()->params.textclass, doclear);
1633 selection.end.pos(pos);
1635 endpar = selection.end.par();
1636 int pos = selection.end.pos();
1637 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1638 selection.start.pos(), pos,
1639 bview->buffer()->params.textclass, doclear);
1641 selection.end.par(endpar);
1642 selection.end.pos(pos);
1643 cursor.pos(selection.end.pos());
1645 endpar = endpar->next();
1647 // sometimes necessary
1649 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1651 redoParagraphs(bview, selection.start, endpar);
1653 // cutSelection can invalidate the cursor so we need to set
1655 cursor = selection.start;
1657 // need a valid cursor. (Lgb)
1658 clearSelection(bview);
1660 setCursor(bview, cursor.par(), cursor.pos());
1661 selection.cursor = cursor;
1662 updateCounters(bview, cursor.row());
1666 void LyXText::copySelection(BufferView * bview)
1668 // Stuff what we got on the clipboard. Even if there is no selection.
1670 // There is a problem with having the stuffing here in that the
1671 // larger the selection the slower LyX will get. This can be
1672 // solved by running the line below only when the selection has
1673 // finished. The solution used currently just works, to make it
1674 // faster we need to be more clever and probably also have more
1675 // calls to stuffClipboard. (Lgb)
1676 bview->stuffClipboard(selectionAsString(bview->buffer()));
1678 // this doesnt make sense, if there is no selection
1679 if (!selection.set())
1682 // ok we have a selection. This is always between selection.start
1683 // and sel_end cursor
1685 // copy behind a space if there is one
1686 while (selection.start.par()->size() > selection.start.pos()
1687 && selection.start.par()->isLineSeparator(selection.start.pos())
1688 && (selection.start.par() != selection.end.par()
1689 || selection.start.pos() < selection.end.pos()))
1690 selection.start.pos(selection.start.pos() + 1);
1692 CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1693 selection.start.pos(), selection.end.pos(),
1694 bview->buffer()->params.textclass);
1698 void LyXText::pasteSelection(BufferView * bview)
1700 // this does not make sense, if there is nothing to paste
1701 if (!CutAndPaste::checkPastePossible(cursor.par()))
1704 setUndo(bview, Undo::INSERT,
1705 cursor.par(), cursor.par()->next());
1708 Paragraph * actpar = cursor.par();
1710 int pos = cursor.pos();
1711 CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1712 bview->buffer()->params.textclass);
1714 redoParagraphs(bview, cursor, endpar);
1716 setCursor(bview, cursor.par(), cursor.pos());
1717 clearSelection(bview);
1719 selection.cursor = cursor;
1720 setCursor(bview, actpar, pos);
1721 setSelection(bview);
1722 updateCounters(bview, cursor.row());
1726 // returns a pointer to the very first Paragraph
1727 Paragraph * LyXText::firstParagraph() const
1729 return ownerParagraph();
1733 // sets the selection over the number of characters of string, no check!!
1734 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1739 selection.cursor = cursor;
1740 for (string::size_type i = 0; i < str.length(); ++i)
1742 setSelection(bview);
1746 // simple replacing. The font of the first selected character is used
1747 void LyXText::replaceSelectionWithString(BufferView * bview,
1750 setCursorParUndo(bview);
1753 if (!selection.set()) { // create a dummy selection
1754 selection.end = cursor;
1755 selection.start = cursor;
1758 // Get font setting before we cut
1759 Paragraph::size_type pos = selection.end.pos();
1760 LyXFont const font = selection.start.par()
1761 ->getFontSettings(bview->buffer()->params,
1762 selection.start.pos());
1764 // Insert the new string
1765 for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1766 selection.end.par()->insertChar(pos, (*cit), font);
1770 // Cut the selection
1771 cutSelection(bview);
1777 // needed to insert the selection
1778 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1780 Paragraph * par = cursor.par();
1781 Paragraph::size_type pos = cursor.pos();
1782 Paragraph * endpar = cursor.par()->next();
1784 setCursorParUndo(bview);
1786 bool isEnvironment =
1787 textclasslist.Style(bview->buffer()->params.textclass,
1788 cursor.par()->getLayout()).isEnvironment();
1790 textclasslist.Style(bview->buffer()->params.textclass,
1791 cursor.par()->getLayout()).free_spacing;
1793 textclasslist.Style(bview->buffer()->params.textclass,
1794 cursor.par()->getLayout()).keepempty;
1796 // only to be sure, should not be neccessary
1797 clearSelection(bview);
1799 // insert the string, don't insert doublespace
1800 bool space_inserted = true;
1801 for(string::const_iterator cit = str.begin();
1802 cit != str.end(); ++cit) {
1804 if (par->size() || keepempty) {
1805 par->breakParagraph(bview->buffer()->params,
1806 pos, isEnvironment);
1809 space_inserted = true;
1813 // do not insert consecutive spaces if !free_spacing
1814 } else if ((*cit == ' ' || *cit == '\t')
1815 && space_inserted && !free_spacing) {
1817 } else if (*cit == '\t') {
1818 if (!free_spacing) {
1819 // tabs are like spaces here
1820 par->insertChar(pos, ' ',
1823 space_inserted = true;
1825 const Paragraph::value_type nb = 8 - pos % 8;
1826 for (Paragraph::size_type a = 0;
1828 par->insertChar(pos, ' ',
1832 space_inserted = true;
1834 } else if (!IsPrintable(*cit)) {
1835 // Ignore unprintables
1838 // just insert the character
1839 par->insertChar(pos, *cit, current_font);
1841 space_inserted = (*cit == ' ');
1846 redoParagraphs(bview, cursor, endpar);
1847 setCursor(bview, cursor.par(), cursor.pos());
1848 selection.cursor = cursor;
1849 setCursor(bview, par, pos);
1850 setSelection(bview);
1854 /* turns double-CR to single CR, others where converted into one
1855 blank. Then InsertStringAsLines is called */
1856 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1858 string linestr(str);
1859 bool newline_inserted = false;
1860 for (string::size_type i = 0; i < linestr.length(); ++i) {
1861 if (linestr[i] == '\n') {
1862 if (newline_inserted) {
1863 // we know that \r will be ignored by
1864 // InsertStringA. Of course, it is a dirty
1865 // trick, but it works...
1866 linestr[i - 1] = '\r';
1870 newline_inserted = true;
1872 } else if (IsPrintable(linestr[i])) {
1873 newline_inserted = false;
1876 insertStringAsLines(bview, linestr);
1880 bool LyXText::gotoNextInset(BufferView * bview,
1881 std::vector<Inset::Code> const & codes,
1882 string const & contents) const
1884 LyXCursor res = cursor;
1887 if (res.pos() < res.par()->size() - 1) {
1888 res.pos(res.pos() + 1);
1890 res.par(res.par()->next());
1894 } while (res.par() &&
1895 !(res.par()->getChar(res.pos()) == Paragraph::META_INSET
1896 && (inset = res.par()->getInset(res.pos())) != 0
1897 && find(codes.begin(), codes.end(), inset->lyxCode())
1899 && (contents.empty() ||
1900 static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1904 setCursor(bview, res.par(), res.pos());
1911 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1912 Paragraph::size_type pos)
1914 LyXCursor tmpcursor;
1917 Paragraph::size_type z;
1918 Row * row = getRow(par, pos, y);
1920 // is there a break one row above
1921 if (row->previous() && row->previous()->par() == row->par()) {
1922 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1923 if (z >= row->pos()) {
1924 // set the dimensions of the row above
1925 y -= row->previous()->height();
1927 refresh_row = row->previous();
1928 status(bview, LyXText::NEED_MORE_REFRESH);
1930 breakAgain(bview, row->previous());
1932 // set the cursor again. Otherwise
1933 // dangling pointers are possible
1934 setCursor(bview, cursor.par(), cursor.pos(),
1935 false, cursor.boundary());
1936 selection.cursor = cursor;
1941 int const tmpheight = row->height();
1942 Paragraph::size_type const tmplast = rowLast(row);
1946 breakAgain(bview, row);
1947 if (row->height() == tmpheight && rowLast(row) == tmplast)
1948 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1950 status(bview, LyXText::NEED_MORE_REFRESH);
1952 // check the special right address boxes
1953 if (textclasslist.Style(bview->buffer()->params.textclass,
1954 par->getLayout()).margintype
1955 == MARGIN_RIGHT_ADDRESS_BOX) {
1962 redoDrawingOfParagraph(bview, tmpcursor);
1965 // set the cursor again. Otherwise dangling pointers are possible
1966 // also set the selection
1968 if (selection.set()) {
1970 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1971 false, selection.cursor.boundary());
1972 selection.cursor = cursor;
1973 setCursorIntern(bview, selection.start.par(),
1974 selection.start.pos(),
1975 false, selection.start.boundary());
1976 selection.start = cursor;
1977 setCursorIntern(bview, selection.end.par(),
1978 selection.end.pos(),
1979 false, selection.end.boundary());
1980 selection.end = cursor;
1981 setCursorIntern(bview, last_sel_cursor.par(),
1982 last_sel_cursor.pos(),
1983 false, last_sel_cursor.boundary());
1984 last_sel_cursor = cursor;
1987 setCursorIntern(bview, cursor.par(), cursor.pos(),
1988 false, cursor.boundary());
1992 // returns false if inset wasn't found
1993 bool LyXText::updateInset(BufferView * bview, Inset * inset)
1995 // first check the current paragraph
1996 int pos = cursor.par()->getPositionOfInset(inset);
1998 checkParagraph(bview, cursor.par(), pos);
2002 // check every paragraph
2004 Paragraph * par = firstParagraph();
2006 pos = par->getPositionOfInset(inset);
2008 checkParagraph(bview, par, pos);
2018 void LyXText::setCursor(BufferView * bview, Paragraph * par,
2019 Paragraph::size_type pos,
2020 bool setfont, bool boundary) const
2022 LyXCursor old_cursor = cursor;
2023 setCursorIntern(bview, par, pos, setfont, boundary);
2024 deleteEmptyParagraphMechanism(bview, old_cursor);
2028 void LyXText::setCursor(BufferView *bview, LyXCursor & cur, Paragraph * par,
2029 Paragraph::size_type pos, bool boundary) const
2033 cur.boundary(boundary);
2035 /* get the cursor y position in text */
2037 Row * row = getRow(par, pos, y);
2038 /* y is now the beginning of the cursor row */
2039 y += row->baseline();
2040 /* y is now the cursor baseline */
2043 /* now get the cursors x position */
2045 float fill_separator;
2047 float fill_label_hfill;
2048 prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2050 Paragraph::size_type cursor_vpos = 0;
2051 Paragraph::size_type last = rowLastPrintable(row);
2053 if (pos > last + 1) // This shouldn't happen.
2055 else if (pos < row->pos())
2058 if (last < row->pos())
2059 cursor_vpos = row->pos();
2060 else if (pos > last && !boundary)
2061 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2062 ? row->pos() : last + 1;
2063 else if (pos > row->pos() &&
2064 (pos > last || boundary))
2065 /// Place cursor after char at (logical) position pos - 1
2066 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2067 ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2069 /// Place cursor before char at (logical) position pos
2070 cursor_vpos = (bidi_level(pos) % 2 == 0)
2071 ? log2vis(pos) : log2vis(pos) + 1;
2073 Paragraph::size_type main_body =
2074 beginningOfMainBody(bview->buffer(), row->par());
2075 if ((main_body > 0) &&
2076 ((main_body-1 > last) ||
2077 !row->par()->isLineSeparator(main_body-1)))
2080 for (Paragraph::size_type vpos = row->pos();
2081 vpos < cursor_vpos; ++vpos) {
2082 pos = vis2log(vpos);
2083 if (main_body > 0 && pos == main_body - 1) {
2084 x += fill_label_hfill +
2085 lyxfont::width(textclasslist.Style(
2086 bview->buffer()->params.textclass,
2087 row->par()->getLayout())
2089 getFont(bview->buffer(), row->par(), -2));
2090 if (row->par()->isLineSeparator(main_body-1))
2091 x -= singleWidth(bview, row->par(),main_body-1);
2093 if (hfillExpansion(bview->buffer(), row, pos)) {
2094 x += singleWidth(bview, row->par(), pos);
2095 if (pos >= main_body)
2098 x += fill_label_hfill;
2099 } else if (row->par()->isSeparator(pos)) {
2100 x += singleWidth(bview, row->par(), pos);
2101 if (pos >= main_body)
2102 x += fill_separator;
2104 x += singleWidth(bview, row->par(), pos);
2113 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2114 Paragraph::size_type pos,
2115 bool setfont, bool boundary) const
2117 InsetText * it = static_cast<InsetText *>(par->InInset());
2118 if (it && (it != inset_owner)) {
2119 it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont,
2122 setCursor(bview, cursor, par, pos, boundary);
2124 setCurrentFont(bview);
2129 void LyXText::setCurrentFont(BufferView * bview) const
2131 Paragraph::size_type pos = cursor.pos();
2132 if (cursor.boundary() && pos > 0)
2136 if (pos == cursor.par()->size())
2138 else // potentional bug... BUG (Lgb)
2139 if (cursor.par()->isSeparator(pos)) {
2140 if (pos > cursor.row()->pos() &&
2141 bidi_level(pos) % 2 ==
2142 bidi_level(pos - 1) % 2)
2144 else if (pos + 1 < cursor.par()->size())
2150 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2151 real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2153 if (cursor.pos() == cursor.par()->size() &&
2154 isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2155 !cursor.boundary()) {
2156 Language const * lang =
2157 cursor.par()->getParLanguage(bview->buffer()->params);
2158 current_font.setLanguage(lang);
2159 current_font.setNumber(LyXFont::OFF);
2160 real_current_font.setLanguage(lang);
2161 real_current_font.setNumber(LyXFont::OFF);
2166 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2168 LyXCursor old_cursor = cursor;
2170 /* get the row first */
2172 Row * row = getRowNearY(y);
2173 cursor.par(row->par());
2176 int column = getColumnNearX(bview, row, x, bound);
2177 cursor.pos(row->pos() + column);
2179 cursor.y(y + row->baseline());
2181 cursor.boundary(bound);
2182 setCurrentFont(bview);
2183 deleteEmptyParagraphMechanism(bview, old_cursor);
2187 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2190 /* get the row first */
2192 Row * row = getRowNearY(y);
2194 int column = getColumnNearX(bview, row, x, bound);
2196 cur.par(row->par());
2197 cur.pos(row->pos() + column);
2199 cur.y(y + row->baseline());
2201 cur.boundary(bound);
2205 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2207 if (cursor.pos() > 0) {
2208 bool boundary = cursor.boundary();
2209 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2210 if (!internal && !boundary &&
2211 isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2212 setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2213 } else if (cursor.par()->previous()) { // steps into the above paragraph.
2214 Paragraph * par = cursor.par()->previous();
2215 setCursor(bview, par, par->size());
2220 void LyXText::cursorRight(BufferView * bview, bool internal) const
2222 if (!internal && cursor.boundary() &&
2223 !cursor.par()->isNewline(cursor.pos()))
2224 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2225 else if (cursor.pos() < cursor.par()->size()) {
2226 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2228 isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2229 setCursor(bview, cursor.par(), cursor.pos(), true, true);
2230 } else if (cursor.par()->next())
2231 setCursor(bview, cursor.par()->next(), 0);
2235 void LyXText::cursorUp(BufferView * bview) const
2237 setCursorFromCoordinates(bview, cursor.x_fix(),
2238 cursor.y() - cursor.row()->baseline() - 1);
2242 void LyXText::cursorDown(BufferView * bview) const
2244 setCursorFromCoordinates(bview, cursor.x_fix(),
2245 cursor.y() - cursor.row()->baseline()
2246 + cursor.row()->height() + 1);
2250 void LyXText::cursorUpParagraph(BufferView * bview) const
2252 if (cursor.pos() > 0) {
2253 setCursor(bview, cursor.par(), 0);
2255 else if (cursor.par()->previous()) {
2256 setCursor(bview, cursor.par()->previous(), 0);
2261 void LyXText::cursorDownParagraph(BufferView * bview) const
2263 if (cursor.par()->next()) {
2264 setCursor(bview, cursor.par()->next(), 0);
2266 setCursor(bview, cursor.par(), cursor.par()->size());
2271 void LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2272 LyXCursor const & old_cursor) const
2274 // Would be wrong to delete anything if we have a selection.
2275 if (selection.set()) return;
2277 // We allow all kinds of "mumbo-jumbo" when freespacing.
2278 if (textclasslist.Style(bview->buffer()->params.textclass,
2279 old_cursor.par()->getLayout()).free_spacing)
2282 bool deleted = false;
2284 /* Ok I'll put some comments here about what is missing.
2285 I have fixed BackSpace (and thus Delete) to not delete
2286 double-spaces automagically. I have also changed Cut,
2287 Copy and Paste to hopefully do some sensible things.
2288 There are still some small problems that can lead to
2289 double spaces stored in the document file or space at
2290 the beginning of paragraphs. This happens if you have
2291 the cursor betwenn to spaces and then save. Or if you
2292 cut and paste and the selection have a space at the
2293 beginning and then save right after the paste. I am
2294 sure none of these are very hard to fix, but I will
2295 put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2296 that I can get some feedback. (Lgb)
2299 // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2300 // delete the LineSeparator.
2303 // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2304 // delete the LineSeparator.
2307 // If the pos around the old_cursor were spaces, delete one of them.
2308 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2309 // Only if the cursor has really moved
2311 if (old_cursor.pos() > 0
2312 && old_cursor.pos() < old_cursor.par()->size()
2313 && old_cursor.par()->isLineSeparator(old_cursor.pos())
2314 && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2315 old_cursor.par()->erase(old_cursor.pos() - 1);
2316 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2318 if (old_cursor.par() == cursor.par() &&
2319 cursor.pos() > old_cursor.pos()) {
2320 setCursorIntern(bview, cursor.par(),
2323 setCursorIntern(bview, cursor.par(),
2329 // Do not delete empty paragraphs with keepempty set.
2330 if ((textclasslist.Style(bview->buffer()->params.textclass,
2331 old_cursor.par()->getLayout())).keepempty)
2334 LyXCursor tmpcursor;
2336 if (old_cursor.par() != cursor.par()) {
2337 if ((old_cursor.par()->size() == 0
2338 || (old_cursor.par()->size() == 1
2339 && old_cursor.par()->isLineSeparator(0)))) {
2340 // ok, we will delete anything
2342 // make sure that you do not delete any environments
2343 status(bview, LyXText::NEED_MORE_REFRESH);
2346 if (old_cursor.row()->previous()) {
2347 refresh_row = old_cursor.row()->previous();
2348 refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2350 cursor = old_cursor; // that undo can restore the right cursor position
2351 Paragraph * endpar = old_cursor.par()->next();
2352 if (endpar && endpar->getDepth()) {
2353 while (endpar && endpar->getDepth()) {
2354 endpar = endpar->next();
2357 setUndo(bview, Undo::DELETE,
2363 removeRow(old_cursor.row());
2364 if (ownerParagraph() == old_cursor.par()) {
2365 ownerParagraph(ownerParagraph()->next());
2368 delete old_cursor.par();
2370 /* Breakagain the next par. Needed
2371 * because of the parindent that
2372 * can occur or dissappear. The
2373 * next row can change its height,
2374 * if there is another layout before */
2375 if (refresh_row->next()) {
2376 breakAgain(bview, refresh_row->next());
2377 updateCounters(bview, refresh_row);
2379 setHeightOfRow(bview, refresh_row);
2381 refresh_row = old_cursor.row()->next();
2382 refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2385 cursor = old_cursor; // that undo can restore the right cursor position
2386 Paragraph * endpar = old_cursor.par()->next();
2387 if (endpar && endpar->getDepth()) {
2388 while (endpar && endpar->getDepth()) {
2389 endpar = endpar->next();
2392 setUndo(bview, Undo::DELETE,
2398 removeRow(old_cursor.row());
2400 if (ownerParagraph() == old_cursor.par()) {
2401 ownerParagraph(ownerParagraph()->next());
2404 delete old_cursor.par();
2406 /* Breakagain the next par. Needed
2407 because of the parindent that can
2408 occur or dissappear.
2409 The next row can change its height,
2410 if there is another layout before
2413 breakAgain(bview, refresh_row);
2414 updateCounters(bview, refresh_row->previous());
2420 setCursorIntern(bview, cursor.par(), cursor.pos());
2422 if (selection.cursor.par() == old_cursor.par()
2423 && selection.cursor.pos() == selection.cursor.pos()) {
2424 // correct selection
2425 selection.cursor = cursor;
2429 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2430 redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2432 setCursorIntern(bview, cursor.par(), cursor.pos());
2433 selection.cursor = cursor;
2440 Paragraph * LyXText::getParFromID(int id) const
2444 Paragraph * result = firstParagraph();
2445 Paragraph * ires = 0;
2446 while (result && result->id() != id) {
2447 if ((ires = result->getParFromID(id)))
2449 result = result->next();
2454 void LyXText::toggleAppendix(BufferView * bview)
2456 Paragraph * par = cursor.par();
2457 bool start = !par->params().startOfAppendix();
2459 // ensure that we have only one start_of_appendix in this document
2460 Paragraph * tmp = firstParagraph();
2461 for (; tmp; tmp = tmp->next())
2462 tmp->params().startOfAppendix(false);
2464 par->params().startOfAppendix(start);
2466 // we can set the refreshing parameters now
2467 status(bview, LyXText::NEED_MORE_REFRESH);
2469 refresh_row = 0; // not needed for full update
2470 updateCounters(bview, 0);
2471 setCursor(bview, cursor.par(), cursor.pos());
2475 Paragraph * LyXText::ownerParagraph() const
2478 return inset_owner->paragraph();
2480 return bv_owner->buffer()->paragraph;
2484 Paragraph * LyXText::ownerParagraph(Paragraph * p) const
2487 inset_owner->paragraph(p);
2489 bv_owner->buffer()->paragraph = p;
2493 Paragraph * LyXText::ownerParagraph(int id, Paragraph * p) const
2495 Paragraph * op = getParFromID(id);
2496 if (op && op->InInset()) {
2497 static_cast<InsetText *>(op->InInset())->paragraph(p);
2500 inset_owner->paragraph(p);
2502 bv_owner->buffer()->paragraph = p;
2508 LyXText::text_status LyXText::status() const
2514 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2516 if ((status_ != NEED_MORE_REFRESH) ||
2517 (status_ == NEED_MORE_REFRESH) && (st != NEED_VERY_LITTLE_REFRESH))
2520 if (inset_owner && st != UNCHANGED)
2521 bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);