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);
125 // this has to be done before the delete
126 text->SetCursor(this, cursor, par, 0);
127 if (par->AutoDeleteInsets()){
129 text->RedoParagraphs(this, cursor,
130 cursor.par()->next());
131 text->FullRebreak(this);
137 // avoid forbidden cursor positions caused by error removing
138 if (tmpcursor.pos() > tmpcursor.par()->Last())
139 tmpcursor.pos(tmpcursor.par()->Last());
140 text->SetCursorIntern(this, tmpcursor.par(), tmpcursor.pos());
146 void BufferView::insertErrors(TeXErrors & terr)
148 // Save the cursor position
149 LyXCursor cursor = text->cursor;
152 // This is drastic, but it's the only fix, I could find. (Asger)
157 for (TeXErrors::Errors::const_iterator cit = terr.begin();
160 string const desctext((*cit).error_desc);
161 string const errortext((*cit).error_text);
162 string const msgtxt = desctext + '\n' + errortext;
163 int const errorrow = (*cit).error_in_line;
165 // Insert error string for row number
169 if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) {
170 buffer()->texrow.increasePos(tmpid, tmppos);
173 LyXParagraph * texrowpar = 0;
176 texrowpar = text->FirstParagraph();
179 texrowpar = text->GetParFromID(tmpid);
185 InsetError * new_inset = new InsetError(msgtxt);
186 text->SetCursorIntern(this, texrowpar, tmppos);
187 text->InsertInset(this, new_inset);
188 text->FullRebreak(this);
190 // Restore the cursor position
191 text->SetCursorIntern(this, cursor.par(), cursor.pos());
195 void BufferView::setCursorFromRow(int row)
200 buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
202 LyXParagraph * texrowpar;
205 texrowpar = text->FirstParagraph();
208 texrowpar = text->GetParFromID(tmpid);
210 text->SetCursor(this, texrowpar, tmppos);
214 bool BufferView::insertInset(Inset * inset, string const & lout,
217 // if we are in a locking inset we should try to insert the
218 // inset there otherwise this is a illegal function now
219 if (theLockingInset()) {
220 if (theLockingInset()->InsertInsetAllowed(inset))
221 return theLockingInset()->InsertInset(this, inset);
225 // not quite sure if we want this...
226 text->SetCursorParUndo(buffer());
231 update(text, BufferView::SELECT|BufferView::FITCUR);
232 text->BreakParagraph(this);
233 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
235 if (text->cursor.par()->Last()) {
236 text->CursorLeft(this);
238 text->BreakParagraph(this);
239 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
242 pair<bool, LyXTextClass::size_type> lres =
243 textclasslist.NumberOfLayout(buffer()->params
245 LyXTextClass::size_type lay;
246 if (lres.first != false) {
250 // layout not fount using default "Standard" (0)
254 text->SetLayout(this, lay);
256 text->SetParagraph(this, 0, 0,
258 VSpace(VSpace::NONE), VSpace(VSpace::NONE),
262 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
264 text->current_font.setLatex(LyXFont::OFF);
267 text->InsertInset(this, inset);
268 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
270 text->UnFreezeUndo();
275 // Open and lock an updatable inset
276 bool BufferView::open_new_inset(UpdatableInset * new_inset, bool behind)
280 if (!insertInset(new_inset)) {
285 LyXFont & font = getLyXText()->real_current_font;
286 new_inset->Edit(this, new_inset->width(this, font), 0, 0);
288 new_inset->Edit(this, 0, 0, 0);
292 /* This is also a buffer property (ale) */
293 // Not so sure about that. a goto Label function can not be buffer local, just
294 // think how this will work in a multiwindo/buffer environment, all the
295 // cursors in all the views showing this buffer will move. (Lgb)
296 // OK, then no cursor action should be allowed in buffer. (ale)
297 bool BufferView::gotoLabel(string const & label)
300 for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
301 it != buffer()->inset_iterator_end(); ++it) {
302 vector<string> labels = (*it)->getLabelList();
303 if (find(labels.begin(),labels.end(),label)
306 text->SetCursor(this, it.getPar(), it.getPos());
307 text->sel_cursor = text->cursor;
308 update(text, BufferView::SELECT|BufferView::FITCUR);
317 void BufferView::allFloats(char flag, char figmar)
319 if (!available()) return;
321 LyXCursor cursor = text->cursor;
324 && cursor.par()->footnoteflag != LyXParagraph::NO_FOOTNOTE
326 && cursor.par()->footnotekind != LyXParagraph::FOOTNOTE
327 && cursor.par()->footnotekind != LyXParagraph::MARGIN
330 && cursor.par()->footnotekind != LyXParagraph::FIG
331 && cursor.par()->footnotekind != LyXParagraph::TAB
332 && cursor.par()->footnotekind != LyXParagraph::WIDE_FIG
333 && cursor.par()->footnotekind != LyXParagraph::WIDE_TAB
334 && cursor.par()->footnotekind != LyXParagraph::ALGORITHM)))
339 LyXCursor tmpcursor = cursor;
340 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
341 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
343 LyXParagraph *par = buffer()->paragraph;
346 if (par->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
348 && par->footnotekind != LyXParagraph::FOOTNOTE
349 && par->footnotekind != LyXParagraph::MARGIN)
351 && par->footnotekind != LyXParagraph::FIG
352 && par->footnotekind != LyXParagraph::TAB
353 && par->footnotekind != LyXParagraph::WIDE_FIG
354 && par->footnotekind != LyXParagraph::WIDE_TAB
355 && par->footnotekind != LyXParagraph::ALGORITHM
360 && par->previous_->footnoteflag !=
361 LyXParagraph::CLOSED_FOOTNOTE){ /* should be */
362 text->SetCursorIntern(this,
365 text->OpenFootnote(this);
369 if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
373 par->footnotekind != LyXParagraph::FOOTNOTE
375 par->footnotekind != LyXParagraph::MARGIN
380 par->footnotekind != LyXParagraph::FIG
382 par->footnotekind != LyXParagraph::TAB
384 par->footnotekind != LyXParagraph::WIDE_FIG
386 par->footnotekind != LyXParagraph::WIDE_TAB
388 par->footnotekind != LyXParagraph::ALGORITHM
392 text->SetCursorIntern(this, par, 0);
393 text->CloseFootnote(this);
399 text->SetCursorIntern(this, cursor.par(), cursor.pos());
406 void BufferView::insertNote()
408 InsetInfo * new_inset = new InsetInfo();
409 insertInset(new_inset);
410 new_inset->Edit(this, 0, 0, 0);
415 void BufferView::openStuff()
418 owner()->getMiniBuffer()->Set(_("Open/Close..."));
421 update(text, BufferView::SELECT|BufferView::FITCUR);
422 text->OpenStuff(this);
423 update(text, BufferView::SELECT|BufferView::FITCUR);
429 void BufferView::toggleFloat()
432 owner()->getMiniBuffer()->Set(_("Open/Close..."));
435 update(text, BufferView::SELECT|BufferView::FITCUR);
436 text->ToggleFootnote(this);
437 update(text, BufferView::SELECT|BufferView::FITCUR);
443 void BufferView::menuUndo()
446 owner()->getMiniBuffer()->Set(_("Undo"));
449 update(text, BufferView::SELECT|BufferView::FITCUR);
450 if (!text->TextUndo(this))
451 owner()->getMiniBuffer()->Set(_("No further undo information"));
453 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
459 void BufferView::menuRedo()
461 if (theLockingInset()) {
462 owner()->getMiniBuffer()->Set(_("Redo not yet supported in math mode"));
467 owner()->getMiniBuffer()->Set(_("Redo"));
470 update(text, BufferView::SELECT|BufferView::FITCUR);
471 if (!text->TextRedo(this))
472 owner()->getMiniBuffer()->Set(_("No further redo information"));
474 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
480 void BufferView::hyphenationPoint()
484 update(text, BufferView::SELECT|BufferView::FITCUR);
485 InsetSpecialChar * new_inset =
486 new InsetSpecialChar(InsetSpecialChar::HYPHENATION);
487 insertInset(new_inset);
492 void BufferView::ldots()
496 update(text, BufferView::SELECT|BufferView::FITCUR);
497 InsetSpecialChar * new_inset =
498 new InsetSpecialChar(InsetSpecialChar::LDOTS);
499 insertInset(new_inset);
504 void BufferView::endOfSentenceDot()
508 update(text, BufferView::SELECT|BufferView::FITCUR);
509 InsetSpecialChar * new_inset =
510 new InsetSpecialChar(InsetSpecialChar::END_OF_SENTENCE);
511 insertInset(new_inset);
516 void BufferView::menuSeparator()
520 update(text, BufferView::SELECT|BufferView::FITCUR);
521 InsetSpecialChar * new_inset =
522 new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR);
523 insertInset(new_inset);
528 void BufferView::newline()
532 update(text, BufferView::SELECT|BufferView::FITCUR);
533 text->InsertChar(this, LyXParagraph::META_NEWLINE);
534 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
539 void BufferView::protectedBlank(LyXText * lt)
543 update(lt, BufferView::SELECT|BufferView::FITCUR);
544 InsetSpecialChar * new_inset =
545 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
546 if (!insertInset(new_inset))
549 updateInset(new_inset, true);
554 void BufferView::hfill()
558 update(text, BufferView::SELECT|BufferView::FITCUR);
559 text->InsertChar(this, LyXParagraph::META_HFILL);
560 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
564 void BufferView::copyEnvironment()
567 text->copyEnvironmentType();
568 // clear the selection, even if mark_set
570 text->ClearSelection(this);
571 update(text, BufferView::SELECT|BufferView::FITCUR);
572 owner()->getMiniBuffer()->Set(_("Paragraph environment type copied"));
577 void BufferView::pasteEnvironment()
580 text->pasteEnvironmentType(this);
581 owner()->getMiniBuffer()->Set(_("Paragraph environment type set"));
582 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
587 void BufferView::copy()
590 text->CopySelection(this);
591 // clear the selection, even if mark_set
593 text->ClearSelection(this);
594 update(text, BufferView::SELECT|BufferView::FITCUR);
595 owner()->getMiniBuffer()->Set(_("Copy"));
599 void BufferView::cut()
603 update(text, BufferView::SELECT|BufferView::FITCUR);
604 text->CutSelection(this);
605 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
606 owner()->getMiniBuffer()->Set(_("Cut"));
611 void BufferView::paste()
613 if (!available()) return;
615 owner()->getMiniBuffer()->Set(_("Paste"));
617 // clear the selection
619 text->ClearSelection(this);
620 update(text, BufferView::SELECT|BufferView::FITCUR);
623 text->PasteSelection(this);
624 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
626 // clear the selection
628 text->ClearSelection(this);
629 update(text, BufferView::SELECT|BufferView::FITCUR);
633 void BufferView::gotoInset(std::vector<Inset::Code> const & codes,
636 if (!available()) return;
640 update(text, BufferView::SELECT|BufferView::FITCUR);
644 text->cursor.par()->GetChar(text->cursor.pos()) == LyXParagraph::META_INSET) {
645 Inset const * inset = text->cursor.par()->GetInset(text->cursor.pos());
646 if (find(codes.begin(), codes.end(), inset->LyxCode())
649 static_cast<InsetCommand const *>(inset)->getContents();
652 if (!text->GotoNextInset(this, codes, contents)) {
653 if (text->cursor.pos()
654 || text->cursor.par() != text->FirstParagraph()) {
655 LyXCursor tmp = text->cursor;
656 text->cursor.par(text->FirstParagraph());
658 if (!text->GotoNextInset(this, codes, contents)) {
660 owner()->getMiniBuffer()->Set(_("No more insets"));
663 owner()->getMiniBuffer()->Set(_("No more insets"));
666 update(text, BufferView::SELECT|BufferView::FITCUR);
667 text->sel_cursor = text->cursor;
671 void BufferView::gotoInset(Inset::Code code, bool same_content)
673 gotoInset(vector<Inset::Code>(1, code), same_content);
677 void BufferView::insertCorrectQuote()
681 if (text->cursor.pos())
682 c = text->cursor.par()->GetChar(text->cursor.pos() - 1);
686 insertInset(new InsetQuotes(c, buffer()->params));
690 /* these functions are for the spellchecker */
691 string const BufferView::nextWord(float & value)
698 return text->SelectNextWord(this, value);
702 void BufferView::selectLastWord()
704 if (!available()) return;
708 text->SelectSelectedWord(this);
709 toggleSelection(false);
710 update(text, BufferView::SELECT|BufferView::FITCUR);
714 void BufferView::endOfSpellCheck()
716 if (!available()) return;
720 text->SelectSelectedWord(this);
721 text->ClearSelection(this);
722 update(text, BufferView::SELECT|BufferView::FITCUR);
726 void BufferView::replaceWord(string const & replacestring)
728 if (!available()) return;
731 update(text, BufferView::SELECT|BufferView::FITCUR);
733 /* clear the selection (if there is any) */
734 toggleSelection(false);
735 update(text, BufferView::SELECT|BufferView::FITCUR);
737 /* clear the selection (if there is any) */
738 toggleSelection(false);
739 text->ReplaceSelectionWithString(this, replacestring);
741 text->SetSelectionOverString(this, replacestring);
743 // Go back so that replacement string is also spellchecked
744 for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
745 text->CursorLeft(this);
747 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
749 // End of spellchecker stuff
752 bool BufferView::lockInset(UpdatableInset * inset)
754 if (!theLockingInset() && inset) {
755 theLockingInset(inset);
758 return theLockingInset()->LockInsetInInset(this, inset);
764 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
766 if (theLockingInset() && available()) {
767 LyXCursor cursor = text->cursor;
768 if ((cursor.pos() - 1 >= 0) &&
769 (cursor.par()->GetChar(cursor.pos() - 1) ==
770 LyXParagraph::META_INSET) &&
771 (cursor.par()->GetInset(cursor.pos() - 1) ==
772 theLockingInset()->GetLockingInset()))
773 text->SetCursor(this, cursor,
774 cursor.par(), cursor.pos() - 1);
775 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
776 LyXText * txt = getLyXText();
777 if (theLockingInset()->GetLockingInset()->LyxCode() ==
779 (txt->real_current_font.language() !=
780 buffer()->params.language
781 || txt->real_current_font.isVisibleRightToLeft()
782 != buffer()->params.language->RightToLeft()))
783 shape = (txt->real_current_font.isVisibleRightToLeft())
784 ? LyXScreen::REVERSED_L_SHAPE
785 : LyXScreen::L_SHAPE;
786 y += cursor.y() + theLockingInset()->InsetInInsetY();
787 pimpl_->screen_->ShowManualCursor(text, x, y, asc, desc,
793 void BufferView::hideLockedInsetCursor()
795 if (theLockingInset() && available()) {
796 pimpl_->screen_->HideCursor();
801 void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
803 if (theLockingInset() && available()) {
804 y += text->cursor.y() + theLockingInset()->InsetInInsetY();
805 if (pimpl_->screen_->FitManualCursor(text, this, x, y, asc, desc))
811 int BufferView::unlockInset(UpdatableInset * inset)
813 if (inset && theLockingInset() == inset) {
814 inset->InsetUnlock(this);
818 } else if (inset && theLockingInset() &&
819 theLockingInset()->UnlockInsetInInset(this, inset)) {
823 return bufferlist.unlockInset(inset);
827 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
829 if (!theLockingInset())
830 return; // shouldn't happen
831 if (kind == Undo::EDIT) // in this case insets would not be stored!
834 text->SetUndo(buffer(), kind,
836 ParFromPos(text->cursor.pos())->previous_,
838 ParFromPos(text->cursor.pos())->next_);
840 text->SetUndo(buffer(), kind,
841 text->cursor.par()->previous(),
842 text->cursor.par()->next());
847 void BufferView::updateInset(Inset * inset, bool mark_dirty)
852 // first check for locking insets
853 if (theLockingInset()) {
854 if (theLockingInset() == inset) {
855 if (text->UpdateInset(this, inset)) {
858 if (buffer()->isLyxClean())
859 owner()->getMiniBuffer()->
861 buffer()->markDirty();
866 } else if (theLockingInset()->UpdateInsetInInset(this,inset)) {
867 if (text->UpdateInset(this, theLockingInset())) {
870 if (buffer()->isLyxClean())
871 owner()->getMiniBuffer()->
873 buffer()->markDirty();
881 // then check the current buffer
884 update(text, BufferView::UPDATE);
885 if (text->UpdateInset(this, inset)) {
887 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
889 update(text, SELECT);
896 bool BufferView::ChangeInsets(Inset::Code code, string const & from, string const & to)
899 LyXParagraph * par = buffer()->paragraph;
900 LyXCursor cursor = text->cursor;
901 LyXCursor tmpcursor = cursor;
903 cursor.par(tmpcursor.par()->ParFromPos(tmpcursor.pos()));
904 cursor.pos(tmpcursor.par()->PositionInParFromPos(tmpcursor.pos()));
906 cursor.par(tmpcursor.par());
907 cursor.pos(tmpcursor.pos());
912 for (LyXParagraph::inset_iterator it = par->inset_iterator_begin();
913 it != par->inset_iterator_end(); ++it) {
914 if ((*it)->LyxCode() == code) {
915 InsetCommand * inset = static_cast<InsetCommand *>(*it);
916 if (inset->getContents() == from) {
917 inset->setContents(to);
925 if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
926 // this is possible now, since SetCursor takes
927 // care about footnotes
928 text->SetCursorIntern(this, par, 0);
929 text->RedoParagraphs(this, text->cursor,
930 text->cursor.par()->next());
931 text->FullRebreak(this);
938 // this is possible now, since SetCursor takes
939 // care about footnotes
940 text->SetCursorIntern(this, par, 0);
941 text->RedoParagraphs(this, text->cursor,
942 text->cursor.par()->next());
943 text->FullRebreak(this);
948 text->SetCursorIntern(this, cursor.par(), cursor.pos());
953 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
955 // Check if the label 'from' appears more than once
956 vector<string> labels = buffer()->getLabelList();
957 if (count(labels.begin(), labels.end(), from) > 1)
960 return ChangeInsets(Inset::REF_CODE, from, to);
964 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
967 vector<pair<string,string> > keys = buffer()->getBibkeyList();
968 if (count_if(keys.begin(), keys.end(),
969 equal_1st_in_pair<string,string>(from))
973 return ChangeInsets(Inset::CITE_CODE, from, to);
976 UpdatableInset * BufferView::theLockingInset() const
978 // If NULL is not allowed we should put an Assert here. (Lgb)
980 return text->the_locking_inset;
985 void BufferView::theLockingInset(UpdatableInset * inset)
987 text->the_locking_inset = inset;
991 LyXText * BufferView::getLyXText() const
993 if (theLockingInset()) {
994 LyXText * txt = theLockingInset()->getLyXText(this, true);
1002 LyXText * BufferView::getParentText(Inset * inset) const
1004 if (inset->owner()) {
1005 LyXText * txt = inset->getLyXText(this);
1006 inset = inset->owner();
1007 while (inset && inset->getLyXText(this) == txt)
1008 inset = inset->owner();
1010 return inset->getLyXText(this);
1015 Language const * BufferView::getParentLanguage(Inset * inset) const
1017 LyXText * text = getParentText(inset);
1018 return text->cursor.par()->GetFontSettings(buffer()->params,
1019 text->cursor.pos()).language();