2 /* This file is part of
3 * ======================================================
5 * LyX, The Document Processor
7 * Copyright 1995 Matthias Ettrich
8 * Copyright 1995-2000 The LyX Team.
10 * ====================================================== */
17 #include "BufferView.h"
19 #include "lyxcursor.h"
21 #include "insets/inseterror.h"
22 #include "insets/insetinfo.h"
23 #include "insets/insetspecialchar.h"
25 #include "bufferlist.h"
26 #include "support/FileInfo.h"
27 #include "lyxscreen.h"
28 #include "support/filetools.h"
29 #include "lyx_gui_misc.h"
31 #include "BufferView_pimpl.h"
32 #include "insets/insetcommand.h" //ChangeRefs
33 #include "support/lyxfunctional.h" //equal_1st_in_pair
37 extern BufferList bufferlist;
47 // Inserts a file into current document
48 bool BufferView::insertLyXFile(string const & filen)
50 // Copyright CHT Software Service GmbH
53 // Insert a Lyxformat - file into current buffer
55 // Moved from lyx_cb.C (Lgb)
57 if (filen.empty()) return false;
59 string const fname = MakeAbsPath(filen);
61 // check if file exist
62 FileInfo const fi(fname);
65 WriteAlert(_("Error!"),
66 _("Specified file is unreadable: "),
67 MakeDisplayPath(fname, 50));
73 ifstream ifs(fname.c_str());
75 WriteAlert(_("Error!"),
76 _("Cannot open specified file: "),
77 MakeDisplayPath(fname, 50));
81 char const c = ifs.peek();
89 lyxerr.debug() << "Will insert file with header" << endl;
90 res = buffer()->readFile(lex, text->cursor.par());
92 lyxerr.debug() << "Will insert file without header" << endl;
93 res = buffer()->readLyXformat2(lex, text->cursor.par());
101 bool BufferView::removeAutoInsets()
103 LyXParagraph * par = buffer()->paragraph;
105 LyXCursor tmpcursor = text->cursor;
111 // this has to be done before the delete
112 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
113 text->SetCursor(this, cursor, par, 0);
114 if (par->AutoDeleteInsets()){
116 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
117 text->RedoParagraphs(this, cursor,
118 cursor.par()->next());
119 text->FullRebreak(this);
125 // avoid forbidden cursor positions caused by error removing
126 if (tmpcursor.pos() > tmpcursor.par()->Last())
127 tmpcursor.pos(tmpcursor.par()->Last());
130 // this has to be done before the delete
131 text->SetCursor(this, cursor, par, 0);
132 if (par->AutoDeleteInsets()){
134 text->RedoParagraphs(this, cursor,
135 cursor.par()->next());
136 text->FullRebreak(this);
141 // avoid forbidden cursor positions caused by error removing
142 if (tmpcursor.pos() > tmpcursor.par()->size())
143 tmpcursor.pos(tmpcursor.par()->size());
145 text->SetCursorIntern(this, tmpcursor.par(), tmpcursor.pos());
151 void BufferView::insertErrors(TeXErrors & terr)
153 // Save the cursor position
154 LyXCursor cursor = text->cursor;
157 // This is drastic, but it's the only fix, I could find. (Asger)
162 for (TeXErrors::Errors::const_iterator cit = terr.begin();
165 string const desctext((*cit).error_desc);
166 string const errortext((*cit).error_text);
167 string const msgtxt = desctext + '\n' + errortext;
168 int const errorrow = (*cit).error_in_line;
170 // Insert error string for row number
174 if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) {
175 buffer()->texrow.increasePos(tmpid, tmppos);
178 LyXParagraph * texrowpar = 0;
181 texrowpar = text->FirstParagraph();
184 texrowpar = text->GetParFromID(tmpid);
190 InsetError * new_inset = new InsetError(msgtxt);
191 text->SetCursorIntern(this, texrowpar, tmppos);
192 text->InsertInset(this, new_inset);
193 text->FullRebreak(this);
195 // Restore the cursor position
196 text->SetCursorIntern(this, cursor.par(), cursor.pos());
200 void BufferView::setCursorFromRow(int row)
205 buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
207 LyXParagraph * texrowpar;
210 texrowpar = text->FirstParagraph();
213 texrowpar = text->GetParFromID(tmpid);
215 text->SetCursor(this, texrowpar, tmppos);
219 bool BufferView::insertInset(Inset * inset, string const & lout,
222 // if we are in a locking inset we should try to insert the
223 // inset there otherwise this is a illegal function now
224 if (theLockingInset()) {
225 if (theLockingInset()->InsertInsetAllowed(inset))
226 return theLockingInset()->InsertInset(this, inset);
230 // not quite sure if we want this...
231 text->SetCursorParUndo(buffer());
236 update(text, BufferView::SELECT|BufferView::FITCUR);
237 text->BreakParagraph(this);
238 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
241 if (text->cursor.par()->Last()) {
243 if (text->cursor.par()->size()) {
245 text->CursorLeft(this);
247 text->BreakParagraph(this);
248 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
251 pair<bool, LyXTextClass::size_type> lres =
252 textclasslist.NumberOfLayout(buffer()->params
254 LyXTextClass::size_type lay;
255 if (lres.first != false) {
259 // layout not fount using default "Standard" (0)
263 text->SetLayout(this, lay);
265 text->SetParagraph(this, 0, 0,
267 VSpace(VSpace::NONE), VSpace(VSpace::NONE),
271 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
273 text->current_font.setLatex(LyXFont::OFF);
276 text->InsertInset(this, inset);
277 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
279 text->UnFreezeUndo();
284 // Open and lock an updatable inset
285 bool BufferView::open_new_inset(UpdatableInset * new_inset, bool behind)
289 if (!insertInset(new_inset)) {
294 LyXFont & font = getLyXText()->real_current_font;
295 new_inset->Edit(this, new_inset->width(this, font), 0, 0);
297 new_inset->Edit(this, 0, 0, 0);
301 /* This is also a buffer property (ale) */
302 // Not so sure about that. a goto Label function can not be buffer local, just
303 // think how this will work in a multiwindo/buffer environment, all the
304 // cursors in all the views showing this buffer will move. (Lgb)
305 // OK, then no cursor action should be allowed in buffer. (ale)
306 bool BufferView::gotoLabel(string const & label)
309 for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
310 it != buffer()->inset_iterator_end(); ++it) {
311 vector<string> labels = (*it)->getLabelList();
312 if (find(labels.begin(),labels.end(),label)
315 text->SetCursor(this, it.getPar(), it.getPos());
316 text->sel_cursor = text->cursor;
317 update(text, BufferView::SELECT|BufferView::FITCUR);
326 void BufferView::allFloats(char flag, char figmar)
328 if (!available()) return;
330 LyXCursor cursor = text->cursor;
333 && cursor.par()->footnoteflag != LyXParagraph::NO_FOOTNOTE
335 && cursor.par()->footnotekind != LyXParagraph::FOOTNOTE
336 && cursor.par()->footnotekind != LyXParagraph::MARGIN
339 && cursor.par()->footnotekind != LyXParagraph::FIG
340 && cursor.par()->footnotekind != LyXParagraph::TAB
341 && cursor.par()->footnotekind != LyXParagraph::WIDE_FIG
342 && cursor.par()->footnotekind != LyXParagraph::WIDE_TAB
343 && cursor.par()->footnotekind != LyXParagraph::ALGORITHM)))
348 LyXCursor tmpcursor = cursor;
349 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
350 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
352 LyXParagraph *par = buffer()->paragraph;
355 if (par->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
357 && par->footnotekind != LyXParagraph::FOOTNOTE
358 && par->footnotekind != LyXParagraph::MARGIN)
360 && par->footnotekind != LyXParagraph::FIG
361 && par->footnotekind != LyXParagraph::TAB
362 && par->footnotekind != LyXParagraph::WIDE_FIG
363 && par->footnotekind != LyXParagraph::WIDE_TAB
364 && par->footnotekind != LyXParagraph::ALGORITHM
369 && par->previous_->footnoteflag !=
370 LyXParagraph::CLOSED_FOOTNOTE){ /* should be */
371 text->SetCursorIntern(this,
374 text->OpenFootnote(this);
378 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
382 par->footnotekind != LyXParagraph::FOOTNOTE
384 par->footnotekind != LyXParagraph::MARGIN
389 par->footnotekind != LyXParagraph::FIG
391 par->footnotekind != LyXParagraph::TAB
393 par->footnotekind != LyXParagraph::WIDE_FIG
395 par->footnotekind != LyXParagraph::WIDE_TAB
397 par->footnotekind != LyXParagraph::ALGORITHM
401 text->SetCursorIntern(this, par, 0);
402 text->CloseFootnote(this);
408 text->SetCursorIntern(this, cursor.par(), cursor.pos());
415 void BufferView::insertNote()
417 InsetInfo * new_inset = new InsetInfo();
418 insertInset(new_inset);
419 new_inset->Edit(this, 0, 0, 0);
424 void BufferView::openStuff()
427 owner()->message(_("Open/Close..."));
430 update(text, BufferView::SELECT|BufferView::FITCUR);
431 text->OpenStuff(this);
432 update(text, BufferView::SELECT|BufferView::FITCUR);
438 void BufferView::toggleFloat()
441 owner()->message(_("Open/Close..."));
444 update(text, BufferView::SELECT|BufferView::FITCUR);
445 text->ToggleFootnote(this);
446 update(text, BufferView::SELECT|BufferView::FITCUR);
452 void BufferView::menuUndo()
455 owner()->message(_("Undo"));
458 update(text, BufferView::SELECT|BufferView::FITCUR);
459 if (!text->TextUndo(this))
460 owner()->message(_("No forther undo information"));
462 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
468 void BufferView::menuRedo()
470 if (theLockingInset()) {
471 owner()->message(_("Redo not yet supported in math mode"));
476 owner()->message(_("Redo"));
479 update(text, BufferView::SELECT|BufferView::FITCUR);
480 if (!text->TextRedo(this))
481 owner()->message(_("No further redo information"));
483 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
489 void BufferView::hyphenationPoint()
493 update(text, BufferView::SELECT|BufferView::FITCUR);
494 InsetSpecialChar * new_inset =
495 new InsetSpecialChar(InsetSpecialChar::HYPHENATION);
496 insertInset(new_inset);
501 void BufferView::ldots()
505 update(text, BufferView::SELECT|BufferView::FITCUR);
506 InsetSpecialChar * new_inset =
507 new InsetSpecialChar(InsetSpecialChar::LDOTS);
508 insertInset(new_inset);
513 void BufferView::endOfSentenceDot()
517 update(text, BufferView::SELECT|BufferView::FITCUR);
518 InsetSpecialChar * new_inset =
519 new InsetSpecialChar(InsetSpecialChar::END_OF_SENTENCE);
520 insertInset(new_inset);
525 void BufferView::menuSeparator()
529 update(text, BufferView::SELECT|BufferView::FITCUR);
530 InsetSpecialChar * new_inset =
531 new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR);
532 insertInset(new_inset);
537 void BufferView::newline()
541 update(text, BufferView::SELECT|BufferView::FITCUR);
542 text->InsertChar(this, LyXParagraph::META_NEWLINE);
543 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
548 void BufferView::protectedBlank(LyXText * lt)
552 update(lt, BufferView::SELECT|BufferView::FITCUR);
553 InsetSpecialChar * new_inset =
554 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
555 if (!insertInset(new_inset))
558 updateInset(new_inset, true);
563 void BufferView::hfill()
567 update(text, BufferView::SELECT|BufferView::FITCUR);
568 text->InsertChar(this, LyXParagraph::META_HFILL);
569 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
574 void BufferView::copyEnvironment()
577 text->copyEnvironmentType();
578 // clear the selection, even if mark_set
580 text->ClearSelection(this);
581 update(text, BufferView::SELECT|BufferView::FITCUR);
582 owner()->message(_("Paragraph environment type copied"));
587 void BufferView::pasteEnvironment()
590 text->pasteEnvironmentType(this);
591 owner()->message(_("Paragraph environment type set"));
592 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
597 void BufferView::copy()
600 text->CopySelection(this);
601 // clear the selection, even if mark_set
603 text->ClearSelection(this);
604 update(text, BufferView::SELECT|BufferView::FITCUR);
605 owner()->message(_("Copy"));
609 void BufferView::cut()
613 update(text, BufferView::SELECT|BufferView::FITCUR);
614 text->CutSelection(this);
615 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
616 owner()->message(_("Cut"));
621 void BufferView::paste()
623 if (!available()) return;
625 owner()->message(_("Paste"));
628 // clear the selection
630 text->ClearSelection(this);
631 update(text, BufferView::SELECT|BufferView::FITCUR);
634 text->PasteSelection(this);
635 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
637 // clear the selection
639 text->ClearSelection(this);
640 update(text, BufferView::SELECT|BufferView::FITCUR);
644 void BufferView::gotoInset(std::vector<Inset::Code> const & codes,
647 if (!available()) return;
651 update(text, BufferView::SELECT|BufferView::FITCUR);
655 text->cursor.par()->GetChar(text->cursor.pos()) == LyXParagraph::META_INSET) {
656 Inset const * inset = text->cursor.par()->GetInset(text->cursor.pos());
657 if (find(codes.begin(), codes.end(), inset->LyxCode())
660 static_cast<InsetCommand const *>(inset)->getContents();
663 if (!text->GotoNextInset(this, codes, contents)) {
664 if (text->cursor.pos()
665 || text->cursor.par() != text->FirstParagraph()) {
666 LyXCursor tmp = text->cursor;
667 text->cursor.par(text->FirstParagraph());
669 if (!text->GotoNextInset(this, codes, contents)) {
671 owner()->message(_("No more insets"));
674 owner()->message(_("No more insets"));
677 update(text, BufferView::SELECT|BufferView::FITCUR);
678 text->sel_cursor = text->cursor;
682 void BufferView::gotoInset(Inset::Code code, bool same_content)
684 gotoInset(vector<Inset::Code>(1, code), same_content);
688 void BufferView::insertCorrectQuote()
692 if (text->cursor.pos())
693 c = text->cursor.par()->GetChar(text->cursor.pos() - 1);
697 insertInset(new InsetQuotes(c, buffer()->params));
701 /* these functions are for the spellchecker */
702 string const BufferView::nextWord(float & value)
709 return text->SelectNextWord(this, value);
713 void BufferView::selectLastWord()
715 if (!available()) return;
719 text->SelectSelectedWord(this);
720 toggleSelection(false);
721 update(text, BufferView::SELECT|BufferView::FITCUR);
725 void BufferView::endOfSpellCheck()
727 if (!available()) return;
731 text->SelectSelectedWord(this);
732 text->ClearSelection(this);
733 update(text, BufferView::SELECT|BufferView::FITCUR);
737 void BufferView::replaceWord(string const & replacestring)
739 if (!available()) return;
742 update(text, BufferView::SELECT|BufferView::FITCUR);
744 /* clear the selection (if there is any) */
745 toggleSelection(false);
746 update(text, BufferView::SELECT|BufferView::FITCUR);
748 /* clear the selection (if there is any) */
749 toggleSelection(false);
750 text->ReplaceSelectionWithString(this, replacestring);
752 text->SetSelectionOverString(this, replacestring);
754 // Go back so that replacement string is also spellchecked
755 for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
756 text->CursorLeft(this);
758 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
760 // End of spellchecker stuff
763 bool BufferView::lockInset(UpdatableInset * inset)
765 if (!theLockingInset() && inset) {
766 theLockingInset(inset);
769 return theLockingInset()->LockInsetInInset(this, inset);
775 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
777 if (theLockingInset() && available()) {
778 LyXCursor cursor = text->cursor;
779 if ((cursor.pos() - 1 >= 0) &&
780 (cursor.par()->GetChar(cursor.pos() - 1) ==
781 LyXParagraph::META_INSET) &&
782 (cursor.par()->GetInset(cursor.pos() - 1) ==
783 theLockingInset()->GetLockingInset()))
784 text->SetCursor(this, cursor,
785 cursor.par(), cursor.pos() - 1);
786 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
787 LyXText * txt = getLyXText();
788 if (theLockingInset()->GetLockingInset()->LyxCode() ==
790 (txt->real_current_font.language() !=
791 buffer()->params.language
792 || txt->real_current_font.isVisibleRightToLeft()
793 != buffer()->params.language->RightToLeft()))
794 shape = (txt->real_current_font.isVisibleRightToLeft())
795 ? LyXScreen::REVERSED_L_SHAPE
796 : LyXScreen::L_SHAPE;
797 y += cursor.y() + theLockingInset()->InsetInInsetY();
798 pimpl_->screen_->ShowManualCursor(text, x, y, asc, desc,
804 void BufferView::hideLockedInsetCursor()
806 if (theLockingInset() && available()) {
807 pimpl_->screen_->HideCursor();
812 void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
814 if (theLockingInset() && available()) {
815 y += text->cursor.y() + theLockingInset()->InsetInInsetY();
816 if (pimpl_->screen_->FitManualCursor(text, this, x, y, asc, desc))
822 int BufferView::unlockInset(UpdatableInset * inset)
824 if (inset && theLockingInset() == inset) {
825 inset->InsetUnlock(this);
829 } else if (inset && theLockingInset() &&
830 theLockingInset()->UnlockInsetInInset(this, inset)) {
834 return bufferlist.unlockInset(inset);
838 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
840 if (!theLockingInset())
841 return; // shouldn't happen
842 if (kind == Undo::EDIT) // in this case insets would not be stored!
845 text->SetUndo(buffer(), kind,
847 ParFromPos(text->cursor.pos())->previous_,
849 ParFromPos(text->cursor.pos())->next_);
851 text->SetUndo(buffer(), kind,
852 text->cursor.par()->previous(),
853 text->cursor.par()->next());
858 void BufferView::updateInset(Inset * inset, bool mark_dirty)
863 // first check for locking insets
864 if (theLockingInset()) {
865 if (theLockingInset() == inset) {
866 if (text->UpdateInset(this, inset)) {
869 buffer()->markDirty();
874 } else if (theLockingInset()->UpdateInsetInInset(this,inset)) {
875 if (text->UpdateInset(this, theLockingInset())) {
878 buffer()->markDirty();
886 // then check the current buffer
889 update(text, BufferView::UPDATE);
890 if (text->UpdateInset(this, inset)) {
892 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
894 update(text, SELECT);
901 bool BufferView::ChangeInsets(Inset::Code code,
902 string const & from, string const & to)
905 LyXParagraph * par = buffer()->paragraph;
906 LyXCursor cursor = text->cursor;
907 LyXCursor tmpcursor = cursor;
909 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
910 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
912 cursor.par(tmpcursor.par());
913 cursor.pos(tmpcursor.pos());
918 for (LyXParagraph::inset_iterator it = par->inset_iterator_begin();
919 it != par->inset_iterator_end(); ++it) {
920 if ((*it)->LyxCode() == code) {
921 InsetCommand * inset = static_cast<InsetCommand *>(*it);
922 if (inset->getContents() == from) {
923 inset->setContents(to);
931 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
932 // this is possible now, since SetCursor takes
933 // care about footnotes
934 text->SetCursorIntern(this, par, 0);
935 text->RedoParagraphs(this, text->cursor,
936 text->cursor.par()->next());
937 text->FullRebreak(this);
944 // this is possible now, since SetCursor takes
945 // care about footnotes
946 text->SetCursorIntern(this, par, 0);
947 text->RedoParagraphs(this, text->cursor,
948 text->cursor.par()->next());
949 text->FullRebreak(this);
954 text->SetCursorIntern(this, cursor.par(), cursor.pos());
959 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
961 // Check if the label 'from' appears more than once
962 vector<string> labels = buffer()->getLabelList();
963 if (count(labels.begin(), labels.end(), from) > 1)
966 return ChangeInsets(Inset::REF_CODE, from, to);
970 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
973 vector<pair<string,string> > keys = buffer()->getBibkeyList();
974 if (count_if(keys.begin(), keys.end(),
975 lyx::equal_1st_in_pair<string,string>(from))
979 return ChangeInsets(Inset::CITE_CODE, from, to);
982 UpdatableInset * BufferView::theLockingInset() const
984 // If NULL is not allowed we should put an Assert here. (Lgb)
986 return text->the_locking_inset;
991 void BufferView::theLockingInset(UpdatableInset * inset)
993 text->the_locking_inset = inset;
997 LyXText * BufferView::getLyXText() const
999 if (theLockingInset()) {
1000 LyXText * txt = theLockingInset()->getLyXText(this, true);
1008 LyXText * BufferView::getParentText(Inset * inset) const
1010 if (inset->owner()) {
1011 LyXText * txt = inset->getLyXText(this);
1012 inset = inset->owner();
1013 while (inset && inset->getLyXText(this) == txt)
1014 inset = inset->owner();
1016 return inset->getLyXText(this);
1022 Language const * BufferView::getParentLanguage(Inset * inset) const
1024 LyXText * text = getParentText(inset);
1025 return text->cursor.par()->GetFontSettings(buffer()->params,
1026 text->cursor.pos()).language();