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 "minibuffer.h"
26 #include "bufferlist.h"
27 #include "support/FileInfo.h"
28 #include "lyxscreen.h"
29 #include "support/filetools.h"
30 #include "lyx_gui_misc.h"
32 #include "BufferView_pimpl.h"
33 #include "insets/insetcommand.h" //ChangeRefs
34 #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()->getMiniBuffer()->Set(_("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()->getMiniBuffer()->Set(_("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()->getMiniBuffer()->Set(_("Undo"));
458 update(text, BufferView::SELECT|BufferView::FITCUR);
459 if (!text->TextUndo(this))
460 owner()->getMiniBuffer()->Set(_("No further undo information"));
462 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
468 void BufferView::menuRedo()
470 if (theLockingInset()) {
471 owner()->getMiniBuffer()->Set(_("Redo not yet supported in math mode"));
476 owner()->getMiniBuffer()->Set(_("Redo"));
479 update(text, BufferView::SELECT|BufferView::FITCUR);
480 if (!text->TextRedo(this))
481 owner()->getMiniBuffer()->Set(_("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);
573 void BufferView::copyEnvironment()
576 text->copyEnvironmentType();
577 // clear the selection, even if mark_set
579 text->ClearSelection(this);
580 update(text, BufferView::SELECT|BufferView::FITCUR);
581 owner()->getMiniBuffer()->Set(_("Paragraph environment type copied"));
586 void BufferView::pasteEnvironment()
589 text->pasteEnvironmentType(this);
590 owner()->getMiniBuffer()->Set(_("Paragraph environment type set"));
591 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
596 void BufferView::copy()
599 text->CopySelection(this);
600 // clear the selection, even if mark_set
602 text->ClearSelection(this);
603 update(text, BufferView::SELECT|BufferView::FITCUR);
604 owner()->getMiniBuffer()->Set(_("Copy"));
608 void BufferView::cut()
612 update(text, BufferView::SELECT|BufferView::FITCUR);
613 text->CutSelection(this);
614 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
615 owner()->getMiniBuffer()->Set(_("Cut"));
620 void BufferView::paste()
622 if (!available()) return;
624 owner()->getMiniBuffer()->Set(_("Paste"));
626 // clear the selection
628 text->ClearSelection(this);
629 update(text, BufferView::SELECT|BufferView::FITCUR);
632 text->PasteSelection(this);
633 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
635 // clear the selection
637 text->ClearSelection(this);
638 update(text, BufferView::SELECT|BufferView::FITCUR);
642 void BufferView::gotoInset(std::vector<Inset::Code> const & codes,
645 if (!available()) return;
649 update(text, BufferView::SELECT|BufferView::FITCUR);
653 text->cursor.par()->GetChar(text->cursor.pos()) == LyXParagraph::META_INSET) {
654 Inset const * inset = text->cursor.par()->GetInset(text->cursor.pos());
655 if (find(codes.begin(), codes.end(), inset->LyxCode())
658 static_cast<InsetCommand const *>(inset)->getContents();
661 if (!text->GotoNextInset(this, codes, contents)) {
662 if (text->cursor.pos()
663 || text->cursor.par() != text->FirstParagraph()) {
664 LyXCursor tmp = text->cursor;
665 text->cursor.par(text->FirstParagraph());
667 if (!text->GotoNextInset(this, codes, contents)) {
669 owner()->getMiniBuffer()->Set(_("No more insets"));
672 owner()->getMiniBuffer()->Set(_("No more insets"));
675 update(text, BufferView::SELECT|BufferView::FITCUR);
676 text->sel_cursor = text->cursor;
680 void BufferView::gotoInset(Inset::Code code, bool same_content)
682 gotoInset(vector<Inset::Code>(1, code), same_content);
686 void BufferView::insertCorrectQuote()
690 if (text->cursor.pos())
691 c = text->cursor.par()->GetChar(text->cursor.pos() - 1);
695 insertInset(new InsetQuotes(c, buffer()->params));
699 /* these functions are for the spellchecker */
700 string const BufferView::nextWord(float & value)
707 return text->SelectNextWord(this, value);
711 void BufferView::selectLastWord()
713 if (!available()) return;
717 text->SelectSelectedWord(this);
718 toggleSelection(false);
719 update(text, BufferView::SELECT|BufferView::FITCUR);
723 void BufferView::endOfSpellCheck()
725 if (!available()) return;
729 text->SelectSelectedWord(this);
730 text->ClearSelection(this);
731 update(text, BufferView::SELECT|BufferView::FITCUR);
735 void BufferView::replaceWord(string const & replacestring)
737 if (!available()) return;
740 update(text, BufferView::SELECT|BufferView::FITCUR);
742 /* clear the selection (if there is any) */
743 toggleSelection(false);
744 update(text, BufferView::SELECT|BufferView::FITCUR);
746 /* clear the selection (if there is any) */
747 toggleSelection(false);
748 text->ReplaceSelectionWithString(this, replacestring);
750 text->SetSelectionOverString(this, replacestring);
752 // Go back so that replacement string is also spellchecked
753 for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
754 text->CursorLeft(this);
756 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
758 // End of spellchecker stuff
761 bool BufferView::lockInset(UpdatableInset * inset)
763 if (!theLockingInset() && inset) {
764 theLockingInset(inset);
767 return theLockingInset()->LockInsetInInset(this, inset);
773 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
775 if (theLockingInset() && available()) {
776 LyXCursor cursor = text->cursor;
777 if ((cursor.pos() - 1 >= 0) &&
778 (cursor.par()->GetChar(cursor.pos() - 1) ==
779 LyXParagraph::META_INSET) &&
780 (cursor.par()->GetInset(cursor.pos() - 1) ==
781 theLockingInset()->GetLockingInset()))
782 text->SetCursor(this, cursor,
783 cursor.par(), cursor.pos() - 1);
784 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
785 LyXText * txt = getLyXText();
786 if (theLockingInset()->GetLockingInset()->LyxCode() ==
788 (txt->real_current_font.language() !=
789 buffer()->params.language
790 || txt->real_current_font.isVisibleRightToLeft()
791 != buffer()->params.language->RightToLeft()))
792 shape = (txt->real_current_font.isVisibleRightToLeft())
793 ? LyXScreen::REVERSED_L_SHAPE
794 : LyXScreen::L_SHAPE;
795 y += cursor.y() + theLockingInset()->InsetInInsetY();
796 pimpl_->screen_->ShowManualCursor(text, x, y, asc, desc,
802 void BufferView::hideLockedInsetCursor()
804 if (theLockingInset() && available()) {
805 pimpl_->screen_->HideCursor();
810 void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
812 if (theLockingInset() && available()) {
813 y += text->cursor.y() + theLockingInset()->InsetInInsetY();
814 if (pimpl_->screen_->FitManualCursor(text, this, x, y, asc, desc))
820 int BufferView::unlockInset(UpdatableInset * inset)
822 if (inset && theLockingInset() == inset) {
823 inset->InsetUnlock(this);
827 } else if (inset && theLockingInset() &&
828 theLockingInset()->UnlockInsetInInset(this, inset)) {
832 return bufferlist.unlockInset(inset);
836 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
838 if (!theLockingInset())
839 return; // shouldn't happen
840 if (kind == Undo::EDIT) // in this case insets would not be stored!
843 text->SetUndo(buffer(), kind,
845 ParFromPos(text->cursor.pos())->previous_,
847 ParFromPos(text->cursor.pos())->next_);
849 text->SetUndo(buffer(), kind,
850 text->cursor.par()->previous(),
851 text->cursor.par()->next());
856 void BufferView::updateInset(Inset * inset, bool mark_dirty)
861 // first check for locking insets
862 if (theLockingInset()) {
863 if (theLockingInset() == inset) {
864 if (text->UpdateInset(this, inset)) {
868 if (buffer()->isLyxClean())
869 owner()->getMiniBuffer()->
872 buffer()->markDirty();
877 } else if (theLockingInset()->UpdateInsetInInset(this,inset)) {
878 if (text->UpdateInset(this, theLockingInset())) {
882 if (buffer()->isLyxClean())
883 owner()->getMiniBuffer()->
886 buffer()->markDirty();
894 // then check the current buffer
897 update(text, BufferView::UPDATE);
898 if (text->UpdateInset(this, inset)) {
900 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
902 update(text, SELECT);
909 bool BufferView::ChangeInsets(Inset::Code code, string const & from, string const & to)
912 LyXParagraph * par = buffer()->paragraph;
913 LyXCursor cursor = text->cursor;
914 LyXCursor tmpcursor = cursor;
916 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
917 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
919 cursor.par(tmpcursor.par());
920 cursor.pos(tmpcursor.pos());
925 for (LyXParagraph::inset_iterator it = par->inset_iterator_begin();
926 it != par->inset_iterator_end(); ++it) {
927 if ((*it)->LyxCode() == code) {
928 InsetCommand * inset = static_cast<InsetCommand *>(*it);
929 if (inset->getContents() == from) {
930 inset->setContents(to);
938 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
939 // this is possible now, since SetCursor takes
940 // care about footnotes
941 text->SetCursorIntern(this, par, 0);
942 text->RedoParagraphs(this, text->cursor,
943 text->cursor.par()->next());
944 text->FullRebreak(this);
951 // this is possible now, since SetCursor takes
952 // care about footnotes
953 text->SetCursorIntern(this, par, 0);
954 text->RedoParagraphs(this, text->cursor,
955 text->cursor.par()->next());
956 text->FullRebreak(this);
961 text->SetCursorIntern(this, cursor.par(), cursor.pos());
966 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
968 // Check if the label 'from' appears more than once
969 vector<string> labels = buffer()->getLabelList();
970 if (count(labels.begin(), labels.end(), from) > 1)
973 return ChangeInsets(Inset::REF_CODE, from, to);
977 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
980 vector<pair<string,string> > keys = buffer()->getBibkeyList();
981 if (count_if(keys.begin(), keys.end(),
982 equal_1st_in_pair<string,string>(from))
986 return ChangeInsets(Inset::CITE_CODE, from, to);
989 UpdatableInset * BufferView::theLockingInset() const
991 // If NULL is not allowed we should put an Assert here. (Lgb)
993 return text->the_locking_inset;
998 void BufferView::theLockingInset(UpdatableInset * inset)
1000 text->the_locking_inset = inset;
1004 LyXText * BufferView::getLyXText() const
1006 if (theLockingInset()) {
1007 LyXText * txt = theLockingInset()->getLyXText(this, true);
1015 LyXText * BufferView::getParentText(Inset * inset) const
1017 if (inset->owner()) {
1018 LyXText * txt = inset->getLyXText(this);
1019 inset = inset->owner();
1020 while (inset && inset->getLyXText(this) == txt)
1021 inset = inset->owner();
1023 return inset->getLyXText(this);
1029 Language const * BufferView::getParentLanguage(Inset * inset) const
1031 LyXText * text = getParentText(inset);
1032 return text->cursor.par()->GetFontSettings(buffer()->params,
1033 text->cursor.pos()).language();