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 text->SetCursor(this, cursor, par, 0);
113 if (par->AutoDeleteInsets()){
115 text->RedoParagraphs(this, cursor,
116 cursor.par()->next());
117 text->FullRebreak(this);
122 // avoid forbidden cursor positions caused by error removing
123 if (tmpcursor.pos() > tmpcursor.par()->size())
124 tmpcursor.pos(tmpcursor.par()->size());
126 text->SetCursorIntern(this, tmpcursor.par(), tmpcursor.pos());
132 void BufferView::insertErrors(TeXErrors & terr)
134 // Save the cursor position
135 LyXCursor cursor = text->cursor;
137 for (TeXErrors::Errors::const_iterator cit = terr.begin();
140 string const desctext((*cit).error_desc);
141 string const errortext((*cit).error_text);
142 string const msgtxt = desctext + '\n' + errortext;
143 int const errorrow = (*cit).error_in_line;
145 // Insert error string for row number
149 if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) {
150 buffer()->texrow.increasePos(tmpid, tmppos);
153 LyXParagraph * texrowpar = 0;
156 texrowpar = text->FirstParagraph();
159 texrowpar = text->GetParFromID(tmpid);
165 InsetError * new_inset = new InsetError(msgtxt);
166 text->SetCursorIntern(this, texrowpar, tmppos);
167 text->InsertInset(this, new_inset);
168 text->FullRebreak(this);
170 // Restore the cursor position
171 text->SetCursorIntern(this, cursor.par(), cursor.pos());
175 void BufferView::setCursorFromRow(int row)
180 buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
182 LyXParagraph * texrowpar;
185 texrowpar = text->FirstParagraph();
188 texrowpar = text->GetParFromID(tmpid);
190 text->SetCursor(this, texrowpar, tmppos);
194 bool BufferView::insertInset(Inset * inset, string const & lout,
197 // if we are in a locking inset we should try to insert the
198 // inset there otherwise this is a illegal function now
199 if (theLockingInset()) {
200 if (theLockingInset()->InsertInsetAllowed(inset))
201 return theLockingInset()->InsertInset(this, inset);
205 // not quite sure if we want this...
206 text->SetCursorParUndo(buffer());
211 update(text, BufferView::SELECT|BufferView::FITCUR);
212 text->BreakParagraph(this);
213 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
215 if (text->cursor.par()->size()) {
216 text->CursorLeft(this);
218 text->BreakParagraph(this);
219 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
222 pair<bool, LyXTextClass::size_type> lres =
223 textclasslist.NumberOfLayout(buffer()->params
225 LyXTextClass::size_type lay;
226 if (lres.first != false) {
230 // layout not fount using default "Standard" (0)
234 text->SetLayout(this, lay);
236 text->SetParagraph(this, 0, 0,
238 VSpace(VSpace::NONE), VSpace(VSpace::NONE),
242 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
244 text->current_font.setLatex(LyXFont::OFF);
247 text->InsertInset(this, inset);
248 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
250 text->UnFreezeUndo();
255 /* This is also a buffer property (ale) */
256 // Not so sure about that. a goto Label function can not be buffer local, just
257 // think how this will work in a multiwindo/buffer environment, all the
258 // cursors in all the views showing this buffer will move. (Lgb)
259 // OK, then no cursor action should be allowed in buffer. (ale)
260 bool BufferView::gotoLabel(string const & label)
263 for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
264 it != buffer()->inset_iterator_end(); ++it) {
265 vector<string> labels = (*it)->getLabelList();
266 if (find(labels.begin(),labels.end(),label)
269 text->SetCursor(this, it.getPar(), it.getPos());
270 text->sel_cursor = text->cursor;
271 update(text, BufferView::SELECT|BufferView::FITCUR);
279 void BufferView::menuUndo()
282 owner()->message(_("Undo"));
285 update(text, BufferView::SELECT|BufferView::FITCUR);
286 if (!text->TextUndo(this))
287 owner()->message(_("No forther undo information"));
289 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
295 void BufferView::menuRedo()
297 if (theLockingInset()) {
298 owner()->message(_("Redo not yet supported in math mode"));
303 owner()->message(_("Redo"));
306 update(text, BufferView::SELECT|BufferView::FITCUR);
307 if (!text->TextRedo(this))
308 owner()->message(_("No further redo information"));
310 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
316 void BufferView::hyphenationPoint()
320 update(text, BufferView::SELECT|BufferView::FITCUR);
321 InsetSpecialChar * new_inset =
322 new InsetSpecialChar(InsetSpecialChar::HYPHENATION);
323 insertInset(new_inset);
328 void BufferView::ldots()
332 update(text, BufferView::SELECT|BufferView::FITCUR);
333 InsetSpecialChar * new_inset =
334 new InsetSpecialChar(InsetSpecialChar::LDOTS);
335 insertInset(new_inset);
340 void BufferView::endOfSentenceDot()
344 update(text, BufferView::SELECT|BufferView::FITCUR);
345 InsetSpecialChar * new_inset =
346 new InsetSpecialChar(InsetSpecialChar::END_OF_SENTENCE);
347 insertInset(new_inset);
352 void BufferView::menuSeparator()
356 update(text, BufferView::SELECT|BufferView::FITCUR);
357 InsetSpecialChar * new_inset =
358 new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR);
359 insertInset(new_inset);
364 void BufferView::newline()
368 update(text, BufferView::SELECT|BufferView::FITCUR);
369 text->InsertChar(this, LyXParagraph::META_NEWLINE);
370 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
375 void BufferView::protectedBlank(LyXText * lt)
379 update(lt, BufferView::SELECT|BufferView::FITCUR);
380 InsetSpecialChar * new_inset =
381 new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
382 if (!insertInset(new_inset))
385 updateInset(new_inset, true);
390 void BufferView::hfill()
394 update(text, BufferView::SELECT|BufferView::FITCUR);
395 text->InsertChar(this, LyXParagraph::META_HFILL);
396 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
401 void BufferView::copyEnvironment()
404 text->copyEnvironmentType();
405 // clear the selection, even if mark_set
407 text->ClearSelection(this);
408 update(text, BufferView::SELECT|BufferView::FITCUR);
409 owner()->message(_("Paragraph environment type copied"));
414 void BufferView::pasteEnvironment()
417 text->pasteEnvironmentType(this);
418 owner()->message(_("Paragraph environment type set"));
419 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
424 void BufferView::copy()
427 text->CopySelection(this);
428 // clear the selection, even if mark_set
430 text->ClearSelection(this);
431 update(text, BufferView::SELECT|BufferView::FITCUR);
432 owner()->message(_("Copy"));
436 void BufferView::cut()
440 update(text, BufferView::SELECT|BufferView::FITCUR);
441 text->CutSelection(this);
442 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
443 owner()->message(_("Cut"));
448 void BufferView::paste()
450 if (!available()) return;
452 owner()->message(_("Paste"));
455 // clear the selection
457 text->ClearSelection(this);
458 update(text, BufferView::SELECT|BufferView::FITCUR);
461 text->PasteSelection(this);
462 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
464 // clear the selection
466 text->ClearSelection(this);
467 update(text, BufferView::SELECT|BufferView::FITCUR);
471 void BufferView::gotoInset(std::vector<Inset::Code> const & codes,
474 if (!available()) return;
478 update(text, BufferView::SELECT|BufferView::FITCUR);
482 text->cursor.par()->GetChar(text->cursor.pos()) == LyXParagraph::META_INSET) {
483 Inset const * inset = text->cursor.par()->GetInset(text->cursor.pos());
484 if (find(codes.begin(), codes.end(), inset->LyxCode())
487 static_cast<InsetCommand const *>(inset)->getContents();
490 if (!text->GotoNextInset(this, codes, contents)) {
491 if (text->cursor.pos()
492 || text->cursor.par() != text->FirstParagraph()) {
493 LyXCursor tmp = text->cursor;
494 text->cursor.par(text->FirstParagraph());
496 if (!text->GotoNextInset(this, codes, contents)) {
498 owner()->message(_("No more insets"));
501 owner()->message(_("No more insets"));
504 update(text, BufferView::SELECT|BufferView::FITCUR);
505 text->sel_cursor = text->cursor;
509 void BufferView::gotoInset(Inset::Code code, bool same_content)
511 gotoInset(vector<Inset::Code>(1, code), same_content);
515 void BufferView::insertCorrectQuote()
519 if (text->cursor.pos())
520 c = text->cursor.par()->GetChar(text->cursor.pos() - 1);
524 insertInset(new InsetQuotes(c, buffer()->params));
528 /* these functions are for the spellchecker */
529 string const BufferView::nextWord(float & value)
536 return text->SelectNextWord(this, value);
540 void BufferView::selectLastWord()
542 if (!available()) return;
546 text->SelectSelectedWord(this);
547 toggleSelection(false);
548 update(text, BufferView::SELECT|BufferView::FITCUR);
552 void BufferView::endOfSpellCheck()
554 if (!available()) return;
558 text->SelectSelectedWord(this);
559 text->ClearSelection(this);
560 update(text, BufferView::SELECT|BufferView::FITCUR);
564 void BufferView::replaceWord(string const & replacestring)
566 if (!available()) return;
569 update(text, BufferView::SELECT|BufferView::FITCUR);
571 /* clear the selection (if there is any) */
572 toggleSelection(false);
573 update(text, BufferView::SELECT|BufferView::FITCUR);
575 /* clear the selection (if there is any) */
576 toggleSelection(false);
577 text->ReplaceSelectionWithString(this, replacestring);
579 text->SetSelectionOverString(this, replacestring);
581 // Go back so that replacement string is also spellchecked
582 for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
583 text->CursorLeft(this);
585 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
587 // End of spellchecker stuff
590 bool BufferView::lockInset(UpdatableInset * inset)
592 if (!theLockingInset() && inset) {
593 theLockingInset(inset);
596 return theLockingInset()->LockInsetInInset(this, inset);
602 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
604 if (theLockingInset() && available()) {
605 LyXCursor cursor = text->cursor;
606 if ((cursor.pos() - 1 >= 0) &&
607 (cursor.par()->GetChar(cursor.pos() - 1) ==
608 LyXParagraph::META_INSET) &&
609 (cursor.par()->GetInset(cursor.pos() - 1) ==
610 theLockingInset()->GetLockingInset()))
611 text->SetCursor(this, cursor,
612 cursor.par(), cursor.pos() - 1);
613 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
614 LyXText * txt = getLyXText();
615 if (theLockingInset()->GetLockingInset()->LyxCode() ==
617 (txt->real_current_font.language() !=
618 buffer()->params.language
619 || txt->real_current_font.isVisibleRightToLeft()
620 != buffer()->params.language->RightToLeft()))
621 shape = (txt->real_current_font.isVisibleRightToLeft())
622 ? LyXScreen::REVERSED_L_SHAPE
623 : LyXScreen::L_SHAPE;
624 y += cursor.y() + theLockingInset()->InsetInInsetY();
625 pimpl_->screen_->ShowManualCursor(text, x, y, asc, desc,
631 void BufferView::hideLockedInsetCursor()
633 if (theLockingInset() && available()) {
634 pimpl_->screen_->HideCursor();
639 void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
641 if (theLockingInset() && available()) {
642 y += text->cursor.y() + theLockingInset()->InsetInInsetY();
643 if (pimpl_->screen_->FitManualCursor(text, this, x, y, asc, desc))
649 int BufferView::unlockInset(UpdatableInset * inset)
651 if (inset && theLockingInset() == inset) {
652 inset->InsetUnlock(this);
656 } else if (inset && theLockingInset() &&
657 theLockingInset()->UnlockInsetInInset(this, inset)) {
661 return bufferlist.unlockInset(inset);
665 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
667 if (!theLockingInset())
668 return; // shouldn't happen
669 if (kind == Undo::EDIT) // in this case insets would not be stored!
671 text->SetUndo(buffer(), kind,
672 text->cursor.par()->previous(),
673 text->cursor.par()->next());
677 void BufferView::updateInset(Inset * inset, bool mark_dirty)
682 // first check for locking insets
683 if (theLockingInset()) {
684 if (theLockingInset() == inset) {
685 if (text->UpdateInset(this, inset)) {
688 buffer()->markDirty();
693 } else if (theLockingInset()->UpdateInsetInInset(this,inset)) {
694 if (text->UpdateInset(this, theLockingInset())) {
697 buffer()->markDirty();
705 // then check the current buffer
708 update(text, BufferView::UPDATE);
709 if (text->UpdateInset(this, inset)) {
711 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
713 update(text, SELECT);
720 bool BufferView::ChangeInsets(Inset::Code code,
721 string const & from, string const & to)
724 LyXParagraph * par = buffer()->paragraph;
725 LyXCursor cursor = text->cursor;
726 LyXCursor tmpcursor = cursor;
727 cursor.par(tmpcursor.par());
728 cursor.pos(tmpcursor.pos());
732 for (LyXParagraph::inset_iterator it = par->inset_iterator_begin();
733 it != par->inset_iterator_end(); ++it) {
734 if ((*it)->LyxCode() == code) {
735 InsetCommand * inset = static_cast<InsetCommand *>(*it);
736 if (inset->getContents() == from) {
737 inset->setContents(to);
744 // this is possible now, since SetCursor takes
745 // care about footnotes
746 text->SetCursorIntern(this, par, 0);
747 text->RedoParagraphs(this, text->cursor,
748 text->cursor.par()->next());
749 text->FullRebreak(this);
753 text->SetCursorIntern(this, cursor.par(), cursor.pos());
758 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
760 // Check if the label 'from' appears more than once
761 vector<string> labels = buffer()->getLabelList();
762 if (count(labels.begin(), labels.end(), from) > 1)
765 return ChangeInsets(Inset::REF_CODE, from, to);
769 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
772 vector<pair<string,string> > keys = buffer()->getBibkeyList();
773 if (count_if(keys.begin(), keys.end(),
774 lyx::equal_1st_in_pair<string,string>(from))
778 return ChangeInsets(Inset::CITE_CODE, from, to);
781 UpdatableInset * BufferView::theLockingInset() const
783 // If NULL is not allowed we should put an Assert here. (Lgb)
785 return text->the_locking_inset;
790 void BufferView::theLockingInset(UpdatableInset * inset)
792 text->the_locking_inset = inset;
796 LyXText * BufferView::getLyXText() const
798 if (theLockingInset()) {
799 LyXText * txt = theLockingInset()->getLyXText(this, true);
807 LyXText * BufferView::getParentText(Inset * inset) const
809 if (inset->owner()) {
810 LyXText * txt = inset->getLyXText(this);
811 inset = inset->owner();
812 while (inset && inset->getLyXText(this) == txt)
813 inset = inset->owner();
815 return inset->getLyXText(this);
821 Language const * BufferView::getParentLanguage(Inset * inset) const
823 LyXText * text = getParentText(inset);
824 return text->cursor.par()->GetFontSettings(buffer()->params,
825 text->cursor.pos()).language();