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
36 extern BufferList bufferlist;
46 // Inserts a file into current document
47 bool BufferView::insertLyXFile(string const & filen)
49 // Copyright CHT Software Service GmbH
52 // Insert a Lyxformat - file into current buffer
54 // Moved from lyx_cb.C (Lgb)
56 if (filen.empty()) return false;
58 string const fname = MakeAbsPath(filen);
60 // check if file exist
61 FileInfo const fi(fname);
64 WriteAlert(_("Error!"),
65 _("Specified file is unreadable: "),
66 MakeDisplayPath(fname, 50));
72 ifstream ifs(fname.c_str());
74 WriteAlert(_("Error!"),
75 _("Cannot open specified file: "),
76 MakeDisplayPath(fname, 50));
80 char const c = ifs.peek();
88 lyxerr.debug() << "Will insert file with header" << endl;
89 res = buffer()->readFile(lex, text->cursor.par());
91 lyxerr.debug() << "Will insert file without header" << endl;
92 res = buffer()->readLyXformat2(lex, text->cursor.par());
100 bool BufferView::removeAutoInsets()
102 LyXParagraph * par = buffer()->paragraph;
104 LyXCursor tmpcursor = text->cursor;
110 // this has to be done before the delete
111 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
112 text->SetCursor(this, cursor, par, 0);
113 if (par->AutoDeleteInsets()){
115 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
116 text->RedoParagraphs(this, cursor,
117 cursor.par()->next());
118 text->FullRebreak(this);
124 // avoid forbidden cursor positions caused by error removing
125 if (tmpcursor.pos() > tmpcursor.par()->Last())
126 tmpcursor.pos(tmpcursor.par()->Last());
129 // this has to be done before the delete
130 text->SetCursor(this, cursor, par, 0);
131 if (par->AutoDeleteInsets()){
133 text->RedoParagraphs(this, cursor,
134 cursor.par()->next());
135 text->FullRebreak(this);
140 // avoid forbidden cursor positions caused by error removing
141 if (tmpcursor.pos() > tmpcursor.par()->size())
142 tmpcursor.pos(tmpcursor.par()->size());
144 text->SetCursorIntern(this, tmpcursor.par(), tmpcursor.pos());
150 void BufferView::insertErrors(TeXErrors & terr)
152 // Save the cursor position
153 LyXCursor cursor = text->cursor;
156 // This is drastic, but it's the only fix, I could find. (Asger)
161 for (TeXErrors::Errors::const_iterator cit = terr.begin();
164 string const desctext((*cit).error_desc);
165 string const errortext((*cit).error_text);
166 string const msgtxt = desctext + '\n' + errortext;
167 int const errorrow = (*cit).error_in_line;
169 // Insert error string for row number
173 if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) {
174 buffer()->texrow.increasePos(tmpid, tmppos);
177 LyXParagraph * texrowpar = 0;
180 texrowpar = text->FirstParagraph();
183 texrowpar = text->GetParFromID(tmpid);
189 InsetError * new_inset = new InsetError(msgtxt);
190 text->SetCursorIntern(this, texrowpar, tmppos);
191 text->InsertInset(this, new_inset);
192 text->FullRebreak(this);
194 // Restore the cursor position
195 text->SetCursorIntern(this, cursor.par(), cursor.pos());
199 void BufferView::setCursorFromRow(int row)
204 buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
206 LyXParagraph * texrowpar;
209 texrowpar = text->FirstParagraph();
212 texrowpar = text->GetParFromID(tmpid);
214 text->SetCursor(this, texrowpar, tmppos);
218 bool BufferView::insertInset(Inset * inset, string const & lout,
221 // if we are in a locking inset we should try to insert the
222 // inset there otherwise this is a illegal function now
223 if (theLockingInset()) {
224 if (theLockingInset()->InsertInsetAllowed(inset))
225 return theLockingInset()->InsertInset(this, inset);
229 // not quite sure if we want this...
230 text->SetCursorParUndo(buffer());
235 update(text, BufferView::SELECT|BufferView::FITCUR);
236 text->BreakParagraph(this);
237 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
240 if (text->cursor.par()->Last()) {
242 if (text->cursor.par()->size()) {
244 text->CursorLeft(this);
246 text->BreakParagraph(this);
247 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
250 pair<bool, LyXTextClass::size_type> lres =
251 textclasslist.NumberOfLayout(buffer()->params
253 LyXTextClass::size_type lay;
254 if (lres.first != false) {
258 // layout not fount using default "Standard" (0)
262 text->SetLayout(this, lay);
264 text->SetParagraph(this, 0, 0,
266 VSpace(VSpace::NONE), VSpace(VSpace::NONE),
270 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
272 text->current_font.setLatex(LyXFont::OFF);
275 text->InsertInset(this, inset);
276 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
278 text->UnFreezeUndo();
283 // Open and lock an updatable inset
284 bool BufferView::open_new_inset(UpdatableInset * new_inset, bool behind)
288 if (!insertInset(new_inset)) {
293 LyXFont & font = getLyXText()->real_current_font;
294 new_inset->Edit(this, new_inset->width(this, font), 0, 0);
296 new_inset->Edit(this, 0, 0, 0);
300 /* This is also a buffer property (ale) */
301 // Not so sure about that. a goto Label function can not be buffer local, just
302 // think how this will work in a multiwindo/buffer environment, all the
303 // cursors in all the views showing this buffer will move. (Lgb)
304 // OK, then no cursor action should be allowed in buffer. (ale)
305 bool BufferView::gotoLabel(string const & label)
308 for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
309 it != buffer()->inset_iterator_end(); ++it) {
310 vector<string> labels = (*it)->getLabelList();
311 if (find(labels.begin(),labels.end(),label)
314 text->SetCursor(this, it.getPar(), it.getPos());
315 text->sel_cursor = text->cursor;
316 update(text, BufferView::SELECT|BufferView::FITCUR);
325 void BufferView::allFloats(char flag, char figmar)
327 if (!available()) return;
329 LyXCursor cursor = text->cursor;
332 && cursor.par()->footnoteflag != LyXParagraph::NO_FOOTNOTE
334 && cursor.par()->footnotekind != LyXParagraph::FOOTNOTE
335 && cursor.par()->footnotekind != LyXParagraph::MARGIN
338 && cursor.par()->footnotekind != LyXParagraph::FIG
339 && cursor.par()->footnotekind != LyXParagraph::TAB
340 && cursor.par()->footnotekind != LyXParagraph::WIDE_FIG
341 && cursor.par()->footnotekind != LyXParagraph::WIDE_TAB
342 && cursor.par()->footnotekind != LyXParagraph::ALGORITHM)))
347 LyXCursor tmpcursor = cursor;
348 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
349 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
351 LyXParagraph *par = buffer()->paragraph;
354 if (par->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
356 && par->footnotekind != LyXParagraph::FOOTNOTE
357 && par->footnotekind != LyXParagraph::MARGIN)
359 && par->footnotekind != LyXParagraph::FIG
360 && par->footnotekind != LyXParagraph::TAB
361 && par->footnotekind != LyXParagraph::WIDE_FIG
362 && par->footnotekind != LyXParagraph::WIDE_TAB
363 && par->footnotekind != LyXParagraph::ALGORITHM
368 && par->previous_->footnoteflag !=
369 LyXParagraph::CLOSED_FOOTNOTE){ /* should be */
370 text->SetCursorIntern(this,
373 text->OpenFootnote(this);
377 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
381 par->footnotekind != LyXParagraph::FOOTNOTE
383 par->footnotekind != LyXParagraph::MARGIN
388 par->footnotekind != LyXParagraph::FIG
390 par->footnotekind != LyXParagraph::TAB
392 par->footnotekind != LyXParagraph::WIDE_FIG
394 par->footnotekind != LyXParagraph::WIDE_TAB
396 par->footnotekind != LyXParagraph::ALGORITHM
400 text->SetCursorIntern(this, par, 0);
401 text->CloseFootnote(this);
407 text->SetCursorIntern(this, cursor.par(), cursor.pos());
414 void BufferView::insertNote()
416 InsetInfo * new_inset = new InsetInfo();
417 insertInset(new_inset);
418 new_inset->Edit(this, 0, 0, 0);
423 void BufferView::openStuff()
426 owner()->getMiniBuffer()->Set(_("Open/Close..."));
429 update(text, BufferView::SELECT|BufferView::FITCUR);
430 text->OpenStuff(this);
431 update(text, BufferView::SELECT|BufferView::FITCUR);
437 void BufferView::toggleFloat()
440 owner()->getMiniBuffer()->Set(_("Open/Close..."));
443 update(text, BufferView::SELECT|BufferView::FITCUR);
444 text->ToggleFootnote(this);
445 update(text, BufferView::SELECT|BufferView::FITCUR);
451 void BufferView::menuUndo()
454 owner()->getMiniBuffer()->Set(_("Undo"));
457 update(text, BufferView::SELECT|BufferView::FITCUR);
458 if (!text->TextUndo(this))
459 owner()->getMiniBuffer()->Set(_("No further undo information"));
461 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
467 void BufferView::menuRedo()
469 if (theLockingInset()) {
470 owner()->getMiniBuffer()->Set(_("Redo not yet supported in math mode"));
475 owner()->getMiniBuffer()->Set(_("Redo"));
478 update(text, BufferView::SELECT|BufferView::FITCUR);
479 if (!text->TextRedo(this))
480 owner()->getMiniBuffer()->Set(_("No further redo information"));
482 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
488 void BufferView::hyphenationPoint()
492 update(text, BufferView::SELECT|BufferView::FITCUR);
493 InsetSpecialChar * new_inset =
494 new InsetSpecialChar(InsetSpecialChar::HYPHENATION);
495 insertInset(new_inset);
500 void BufferView::ldots()
504 update(text, BufferView::SELECT|BufferView::FITCUR);
505 InsetSpecialChar * new_inset =
506 new InsetSpecialChar(InsetSpecialChar::LDOTS);
507 insertInset(new_inset);
512 void BufferView::endOfSentenceDot()
516 update(text, BufferView::SELECT|BufferView::FITCUR);
517 InsetSpecialChar * new_inset =
518 new InsetSpecialChar(InsetSpecialChar::END_OF_SENTENCE);
519 insertInset(new_inset);
524 void BufferView::menuSeparator()
528 update(text, BufferView::SELECT|BufferView::FITCUR);
529 InsetSpecialChar * new_inset =
530 new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR);
531 insertInset(new_inset);
536 void BufferView::newline()
540 update(text, BufferView::SELECT|BufferView::FITCUR);
541 text->InsertChar(this, LyXParagraph::META_NEWLINE);
542 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
547 void BufferView::protectedBlank(LyXText * lt)
551 update(lt, BufferView::SELECT|BufferView::FITCUR);
552 InsetSpecialChar * new_inset =
553 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
554 if (!insertInset(new_inset))
557 updateInset(new_inset, true);
562 void BufferView::hfill()
566 update(text, BufferView::SELECT|BufferView::FITCUR);
567 text->InsertChar(this, LyXParagraph::META_HFILL);
568 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
572 void BufferView::copyEnvironment()
575 text->copyEnvironmentType();
576 // clear the selection, even if mark_set
578 text->ClearSelection(this);
579 update(text, BufferView::SELECT|BufferView::FITCUR);
580 owner()->getMiniBuffer()->Set(_("Paragraph environment type copied"));
585 void BufferView::pasteEnvironment()
588 text->pasteEnvironmentType(this);
589 owner()->getMiniBuffer()->Set(_("Paragraph environment type set"));
590 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
595 void BufferView::copy()
598 text->CopySelection(this);
599 // clear the selection, even if mark_set
601 text->ClearSelection(this);
602 update(text, BufferView::SELECT|BufferView::FITCUR);
603 owner()->getMiniBuffer()->Set(_("Copy"));
607 void BufferView::cut()
611 update(text, BufferView::SELECT|BufferView::FITCUR);
612 text->CutSelection(this);
613 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
614 owner()->getMiniBuffer()->Set(_("Cut"));
619 void BufferView::paste()
621 if (!available()) return;
623 owner()->getMiniBuffer()->Set(_("Paste"));
625 // clear the selection
627 text->ClearSelection(this);
628 update(text, BufferView::SELECT|BufferView::FITCUR);
631 text->PasteSelection(this);
632 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
634 // clear the selection
636 text->ClearSelection(this);
637 update(text, BufferView::SELECT|BufferView::FITCUR);
641 void BufferView::gotoInset(std::vector<Inset::Code> const & codes,
644 if (!available()) return;
648 update(text, BufferView::SELECT|BufferView::FITCUR);
652 text->cursor.par()->GetChar(text->cursor.pos()) == LyXParagraph::META_INSET) {
653 Inset const * inset = text->cursor.par()->GetInset(text->cursor.pos());
654 if (find(codes.begin(), codes.end(), inset->LyxCode())
657 static_cast<InsetCommand const *>(inset)->getContents();
660 if (!text->GotoNextInset(this, codes, contents)) {
661 if (text->cursor.pos()
662 || text->cursor.par() != text->FirstParagraph()) {
663 LyXCursor tmp = text->cursor;
664 text->cursor.par(text->FirstParagraph());
666 if (!text->GotoNextInset(this, codes, contents)) {
668 owner()->getMiniBuffer()->Set(_("No more insets"));
671 owner()->getMiniBuffer()->Set(_("No more insets"));
674 update(text, BufferView::SELECT|BufferView::FITCUR);
675 text->sel_cursor = text->cursor;
679 void BufferView::gotoInset(Inset::Code code, bool same_content)
681 gotoInset(vector<Inset::Code>(1, code), same_content);
685 void BufferView::insertCorrectQuote()
689 if (text->cursor.pos())
690 c = text->cursor.par()->GetChar(text->cursor.pos() - 1);
694 insertInset(new InsetQuotes(c, buffer()->params));
698 /* these functions are for the spellchecker */
699 string const BufferView::nextWord(float & value)
706 return text->SelectNextWord(this, value);
710 void BufferView::selectLastWord()
712 if (!available()) return;
716 text->SelectSelectedWord(this);
717 toggleSelection(false);
718 update(text, BufferView::SELECT|BufferView::FITCUR);
722 void BufferView::endOfSpellCheck()
724 if (!available()) return;
728 text->SelectSelectedWord(this);
729 text->ClearSelection(this);
730 update(text, BufferView::SELECT|BufferView::FITCUR);
734 void BufferView::replaceWord(string const & replacestring)
736 if (!available()) return;
739 update(text, BufferView::SELECT|BufferView::FITCUR);
741 /* clear the selection (if there is any) */
742 toggleSelection(false);
743 update(text, BufferView::SELECT|BufferView::FITCUR);
745 /* clear the selection (if there is any) */
746 toggleSelection(false);
747 text->ReplaceSelectionWithString(this, replacestring);
749 text->SetSelectionOverString(this, replacestring);
751 // Go back so that replacement string is also spellchecked
752 for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
753 text->CursorLeft(this);
755 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
757 // End of spellchecker stuff
760 bool BufferView::lockInset(UpdatableInset * inset)
762 if (!theLockingInset() && inset) {
763 theLockingInset(inset);
766 return theLockingInset()->LockInsetInInset(this, inset);
772 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
774 if (theLockingInset() && available()) {
775 LyXCursor cursor = text->cursor;
776 if ((cursor.pos() - 1 >= 0) &&
777 (cursor.par()->GetChar(cursor.pos() - 1) ==
778 LyXParagraph::META_INSET) &&
779 (cursor.par()->GetInset(cursor.pos() - 1) ==
780 theLockingInset()->GetLockingInset()))
781 text->SetCursor(this, cursor,
782 cursor.par(), cursor.pos() - 1);
783 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
784 LyXText * txt = getLyXText();
785 if (theLockingInset()->GetLockingInset()->LyxCode() ==
787 (txt->real_current_font.language() !=
788 buffer()->params.language
789 || txt->real_current_font.isVisibleRightToLeft()
790 != buffer()->params.language->RightToLeft()))
791 shape = (txt->real_current_font.isVisibleRightToLeft())
792 ? LyXScreen::REVERSED_L_SHAPE
793 : LyXScreen::L_SHAPE;
794 y += cursor.y() + theLockingInset()->InsetInInsetY();
795 pimpl_->screen_->ShowManualCursor(text, x, y, asc, desc,
801 void BufferView::hideLockedInsetCursor()
803 if (theLockingInset() && available()) {
804 pimpl_->screen_->HideCursor();
809 void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
811 if (theLockingInset() && available()) {
812 y += text->cursor.y() + theLockingInset()->InsetInInsetY();
813 if (pimpl_->screen_->FitManualCursor(text, this, x, y, asc, desc))
819 int BufferView::unlockInset(UpdatableInset * inset)
821 if (inset && theLockingInset() == inset) {
822 inset->InsetUnlock(this);
826 } else if (inset && theLockingInset() &&
827 theLockingInset()->UnlockInsetInInset(this, inset)) {
831 return bufferlist.unlockInset(inset);
835 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
837 if (!theLockingInset())
838 return; // shouldn't happen
839 if (kind == Undo::EDIT) // in this case insets would not be stored!
842 text->SetUndo(buffer(), kind,
844 ParFromPos(text->cursor.pos())->previous_,
846 ParFromPos(text->cursor.pos())->next_);
848 text->SetUndo(buffer(), kind,
849 text->cursor.par()->previous(),
850 text->cursor.par()->next());
855 void BufferView::updateInset(Inset * inset, bool mark_dirty)
860 // first check for locking insets
861 if (theLockingInset()) {
862 if (theLockingInset() == inset) {
863 if (text->UpdateInset(this, inset)) {
866 if (buffer()->isLyxClean())
867 owner()->getMiniBuffer()->
869 buffer()->markDirty();
874 } else if (theLockingInset()->UpdateInsetInInset(this,inset)) {
875 if (text->UpdateInset(this, theLockingInset())) {
878 if (buffer()->isLyxClean())
879 owner()->getMiniBuffer()->
881 buffer()->markDirty();
889 // then check the current buffer
892 update(text, BufferView::UPDATE);
893 if (text->UpdateInset(this, inset)) {
895 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
897 update(text, SELECT);
904 bool BufferView::ChangeInsets(Inset::Code code, string const & from, string const & to)
907 LyXParagraph * par = buffer()->paragraph;
908 LyXCursor cursor = text->cursor;
909 LyXCursor tmpcursor = cursor;
911 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
912 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
914 cursor.par(tmpcursor.par());
915 cursor.pos(tmpcursor.pos());
920 for (LyXParagraph::inset_iterator it = par->inset_iterator_begin();
921 it != par->inset_iterator_end(); ++it) {
922 if ((*it)->LyxCode() == code) {
923 InsetCommand * inset = static_cast<InsetCommand *>(*it);
924 if (inset->getContents() == from) {
925 inset->setContents(to);
933 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
934 // this is possible now, since SetCursor takes
935 // care about footnotes
936 text->SetCursorIntern(this, par, 0);
937 text->RedoParagraphs(this, text->cursor,
938 text->cursor.par()->next());
939 text->FullRebreak(this);
946 // this is possible now, since SetCursor takes
947 // care about footnotes
948 text->SetCursorIntern(this, par, 0);
949 text->RedoParagraphs(this, text->cursor,
950 text->cursor.par()->next());
951 text->FullRebreak(this);
956 text->SetCursorIntern(this, cursor.par(), cursor.pos());
961 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
963 // Check if the label 'from' appears more than once
964 vector<string> labels = buffer()->getLabelList();
965 if (count(labels.begin(), labels.end(), from) > 1)
968 return ChangeInsets(Inset::REF_CODE, from, to);
972 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
975 vector<pair<string,string> > keys = buffer()->getBibkeyList();
976 if (count_if(keys.begin(), keys.end(),
977 equal_1st_in_pair<string,string>(from))
981 return ChangeInsets(Inset::CITE_CODE, from, to);
984 UpdatableInset * BufferView::theLockingInset() const
986 // If NULL is not allowed we should put an Assert here. (Lgb)
988 return text->the_locking_inset;
993 void BufferView::theLockingInset(UpdatableInset * inset)
995 text->the_locking_inset = inset;
999 LyXText * BufferView::getLyXText() const
1001 if (theLockingInset()) {
1002 LyXText * txt = theLockingInset()->getLyXText(this, true);
1010 LyXText * BufferView::getParentText(Inset * inset) const
1012 if (inset->owner()) {
1013 LyXText * txt = inset->getLyXText(this);
1014 inset = inset->owner();
1015 while (inset && inset->getLyXText(this) == txt)
1016 inset = inset->owner();
1018 return inset->getLyXText(this);
1023 Language const * BufferView::getParentLanguage(Inset * inset) const
1025 LyXText * text = getParentText(inset);
1026 return text->cursor.par()->GetFontSettings(buffer()->params,
1027 text->cursor.pos()).language();