]> git.lyx.org Git - lyx.git/blob - src/BufferView.C
move BoostFormat and boost-inst
[lyx.git] / src / BufferView.C
1 /**
2  * \file BufferView.C
3  * Copyright 1995-2002 the LyX Team
4  * Read the file COPYING
5  *
6  * \author unknown
7  * \author John Levon <moz@compsoc.man.ac.uk>
8  */
9
10 #include <config.h>
11
12 #include "BufferView.h"
13 #include "BufferView_pimpl.h"
14
15 #include "BufferView_pimpl.h"
16 #include "LaTeX.h"
17 #include "ParagraphParameters.h"
18 #include "WordLangTuple.h"
19 #include "buffer.h"
20 #include "bufferlist.h"
21 #include "debug.h"
22 #include "gettext.h"
23 #include "iterators.h"
24 #include "language.h"
25 #include "lyxcursor.h"
26 #include "lyxlex.h"
27 #include "lyxtext.h"
28 #include "undo_funcs.h"
29 #include "changes.h"
30
31 #include "frontends/Alert.h"
32 #include "frontends/Dialogs.h"
33 #include "frontends/LyXView.h"
34 #include "frontends/WorkArea.h"
35 #include "frontends/screen.h"
36
37 #include "insets/insetcommand.h" // ChangeRefs
38 #include "insets/inseterror.h"
39 #include "insets/updatableinset.h"
40
41 #include "support/FileInfo.h"
42 #include "support/filetools.h"
43 #include "support/lyxfunctional.h" // equal_1st_in_pair
44 #include "support/types.h"
45 #include "support/lyxalgo.h" // lyx_count
46 #include "support/BoostFormat.h"
47
48 #include <fstream>
49
50 extern BufferList bufferlist;
51
52 using lyx::pos_type;
53
54 using std::pair;
55 using std::endl;
56 using std::ifstream;
57 using std::vector;
58 using std::find;
59 using std::count_if;
60
61
62 BufferView::BufferView(LyXView * owner, int xpos, int ypos,
63                        int width, int height)
64         : pimpl_(new Pimpl(this, owner, xpos, ypos, width, height))
65 {
66         text = 0;
67 }
68
69
70 BufferView::~BufferView()
71 {
72         delete text;
73         delete pimpl_;
74 }
75
76
77 Buffer * BufferView::buffer() const
78 {
79         return pimpl_->buffer_;
80 }
81
82
83 LyXScreen & BufferView::screen() const
84 {
85         return pimpl_->screen();
86 }
87
88
89 LyXView * BufferView::owner() const
90 {
91         return pimpl_->owner_;
92 }
93
94
95 Painter & BufferView::painter() const
96 {
97         return pimpl_->painter();
98 }
99
100
101 void BufferView::buffer(Buffer * b)
102 {
103         pimpl_->buffer(b);
104 }
105
106
107 void BufferView::reload()
108 {
109         string const fn = buffer()->fileName();
110         if (bufferlist.close(buffer(), false))
111                 buffer(bufferlist.loadLyXFile(fn));
112 }
113
114
115 void BufferView::resize()
116 {
117         if (pimpl_->buffer_) {
118                 pimpl_->resizeCurrentBuffer();
119         }
120 }
121
122
123 void BufferView::repaint()
124 {
125         pimpl_->repaint();
126 }
127
128
129 bool BufferView::fitCursor()
130 {
131         return pimpl_->fitCursor();
132 }
133
134
135 void BufferView::update()
136 {
137         pimpl_->update();
138 }
139
140
141 void BufferView::updateScrollbar()
142 {
143         pimpl_->updateScrollbar();
144 }
145
146
147 void BufferView::scrollDocView(int value)
148 {
149         pimpl_->scrollDocView(value);
150 }
151
152
153 void BufferView::redoCurrentBuffer()
154 {
155         pimpl_->redoCurrentBuffer();
156 }
157
158
159 bool BufferView::available() const
160 {
161         return pimpl_->available();
162 }
163
164
165 Change const BufferView::getCurrentChange()
166 {
167         return pimpl_->getCurrentChange();
168 }
169
170
171 void BufferView::beforeChange(LyXText * text)
172 {
173         pimpl_->beforeChange(text);
174 }
175
176
177 void BufferView::savePosition(unsigned int i)
178 {
179         pimpl_->savePosition(i);
180 }
181
182
183 void BufferView::restorePosition(unsigned int i)
184 {
185         pimpl_->restorePosition(i);
186 }
187
188
189 bool BufferView::isSavedPosition(unsigned int i)
190 {
191         return pimpl_->isSavedPosition(i);
192 }
193
194
195 void BufferView::update(LyXText * text, UpdateCodes f)
196 {
197         pimpl_->update(text, f);
198 }
199
200
201 void BufferView::update(UpdateCodes f)
202 {
203         pimpl_->update(f);
204 }
205
206
207 void BufferView::switchKeyMap()
208 {
209         pimpl_->switchKeyMap();
210 }
211
212
213 void BufferView::insetUnlock()
214 {
215         pimpl_->insetUnlock();
216 }
217
218
219 int BufferView::workWidth() const
220 {
221         return pimpl_->workarea().workWidth();
222 }
223
224
225 void BufferView::showCursor()
226 {
227         pimpl_->showCursor();
228 }
229
230
231 void BufferView::hideCursor()
232 {
233         pimpl_->hideCursor();
234 }
235
236
237 void BufferView::toggleSelection(bool b)
238 {
239         pimpl_->toggleSelection(b);
240 }
241
242
243 void BufferView::toggleToggle()
244 {
245         pimpl_->toggleToggle();
246 }
247
248
249 void BufferView::center()
250 {
251         pimpl_->center();
252 }
253
254
255 string const BufferView::getClipboard() const
256 {
257         return pimpl_->workarea().getClipboard();
258 }
259
260
261 void BufferView::stuffClipboard(string const & stuff) const
262 {
263         pimpl_->stuffClipboard(stuff);
264 }
265
266
267 BufferView::UpdateCodes operator|(BufferView::UpdateCodes uc1,
268                                   BufferView::UpdateCodes uc2)
269 {
270         return static_cast<BufferView::UpdateCodes>
271                 (static_cast<int>(uc1) | static_cast<int>(uc2));
272 }
273
274
275 bool BufferView::dispatch(FuncRequest const & ev)
276 {
277         return pimpl_->dispatch(ev);
278 }
279
280
281 void BufferView::scroll(int lines)
282 {
283         pimpl_->scroll(lines);
284 }
285
286
287 // Inserts a file into current document
288 bool BufferView::insertLyXFile(string const & filen)
289         //
290         // Copyright CHT Software Service GmbH
291         // Uwe C. Schroeder
292         //
293         // Insert a LyXformat - file into current buffer
294         //
295         // Moved from lyx_cb.C (Lgb)
296 {
297         if (filen.empty())
298                 return false;
299
300         string const fname = MakeAbsPath(filen);
301
302         // check if file exist
303         FileInfo const fi(fname);
304
305         if (!fi.readable()) {
306                 string const file = MakeDisplayPath(fname, 50);
307 #if USE_BOOST_FORMAT
308                 boost::format fmt(_("The specified document\n%1$s\ncould not be read."));
309                 fmt % file;
310                 string text = fmt.str();
311 #else
312                 string text = _("The specified document\n");
313                 text += file + _(" could not be read.");
314 #endif
315                 Alert::error(_("Could not read document"), text);
316                 return false;
317         }
318
319         beforeChange(text);
320
321         ifstream ifs(fname.c_str());
322         if (!ifs) {
323                 string const file = MakeDisplayPath(fname, 50);
324 #if USE_BOOST_FORMAT
325                 boost::format fmt(_("Could not open the specified document\n%1$s."));
326                 fmt % file;
327                 string text = fmt.str();
328 #else
329                 string text = _("Could not open the specified document\n");
330                 text += file + ".";
331 #endif
332                 Alert::error(_("Could not open file"), text);
333                 return false;
334         }
335
336         int const c = ifs.peek();
337
338         LyXLex lex(0, 0);
339         lex.setStream(ifs);
340
341         bool res = true;
342
343         if (c == '#') {
344                 // FIXME: huh ? No we won't !
345                 lyxerr[Debug::INFO] << "Will insert file with header" << endl;
346                 res = buffer()->readFile(lex, fname, ParagraphList::iterator(text->cursor.par()));
347         } else {
348                 lyxerr[Debug::INFO] << "Will insert file without header"
349                                     << endl;
350                 res = buffer()->readBody(lex, ParagraphList::iterator(text->cursor.par()));
351         }
352
353         resize();
354         return res;
355 }
356
357
358 bool BufferView::removeAutoInsets()
359 {
360         // keep track of which pos and par the cursor was on
361         Paragraph * cursor_par = text->cursor.par();
362         Paragraph * cursor_par_prev = cursor_par ? cursor_par->previous() : 0;
363         Paragraph * cursor_par_next = cursor_par ? cursor_par->next() : 0;
364         pos_type cursor_pos = text->cursor.pos();
365
366         bool found = false;
367
368         // Trap the deletion of the paragraph the cursor is in.
369         // Iterate until we find a paragraph that won't be immediately deleted.
370         // In reality this should mean we only execute the body of the while
371         // loop once at most.  However for safety we iterate rather than just
372         // make this an if () conditional.
373         while ((cursor_par_prev || cursor_par_next)
374                && text->setCursor(
375                                   cursor_par_prev ? cursor_par_prev : cursor_par_next,
376                                   0)) {
377                 // We just removed cursor_par so have to fix the "cursor"
378                 if (cursor_par_prev) {
379                         // '.' = cursor_par
380                         //  a -> a.
381                         // .
382                         cursor_par = cursor_par_prev;
383                         cursor_pos = cursor_par->size();
384                 } else {
385                         // .  -> .a
386                         //  a
387                         cursor_par = cursor_par_next;
388                         cursor_pos = 0;
389                 }
390                 cursor_par_prev = cursor_par->previous();
391                 cursor_par_next = cursor_par->next();
392         }
393
394         // Iterate through the paragraphs removing autoDelete insets as we go.
395         // If the paragraph ends up empty after all the autoDelete insets are
396         // removed that paragraph will be removed by the next setCursor() call.
397         ParIterator it = buffer()->par_iterator_begin();
398         ParIterator end = buffer()->par_iterator_end();
399         for (; it != end; ++it) {
400                 Paragraph * par = *it;
401                 Paragraph * par_prev = par ? par->previous() : 0;
402                 bool removed = false;
403
404                 if (text->setCursor(par, 0)
405                     && cursor_par == par_prev) {
406                         // The previous setCursor line was deleted and that
407                         // was the cursor_par line.  This can only happen if an
408                         // error box was the sole item on cursor_par.
409                         // It is possible for cursor_par_prev to be stray if
410                         // the line it pointed to only had a error box on it
411                         // so we have to set it to a known correct value.
412                         // This is often the same value it already had.
413                         cursor_par_prev = par->previous();
414                         if (cursor_par_prev) {
415                                 // '|' = par, '.' = cursor_par, 'E' = error box
416                                 // First step below may occur before while{}
417                                 //  a    |a      a     a     a.
418                                 //  E -> .E -> |.E -> .  -> |b
419                                 // .      b      b    |b
420                                 //  b
421                                 cursor_par = cursor_par_prev;
422                                 cursor_pos = cursor_par_prev->size();
423                                 cursor_par_prev = cursor_par->previous();
424                                 // cursor_par_next remains the same
425                         } else if (cursor_par_next) {
426                                 // First step below may occur before while{}
427                                 // .
428                                 //  E -> |.E -> |.  -> . -> .|a
429                                 //  a      a      a    |a
430                                 cursor_par = cursor_par_next;
431                                 cursor_pos = 0;
432                                 // cursor_par_prev remains unset
433                                 cursor_par_next = cursor_par->next();
434                         } else {
435                                 // I can't find a way to trigger this
436                                 // so it should be unreachable code
437                                 // unless the buffer is corrupted.
438                                 lyxerr << "BufferView::removeAutoInsets() is bad\n";
439                         }
440                 }
441
442                 InsetList::iterator pit = par->insetlist.begin();
443                 InsetList::iterator pend = par->insetlist.end();
444
445                 while (pit != pend) {
446                         if (pit.getInset()->autoDelete()) {
447                                 removed = true;
448                                 pos_type const pos = pit.getPos();
449
450                                 par->erase(pos);
451                                 // We just invalidated par's inset iterators so
452                                 // we get the next valid iterator position
453                                 pit = par->insetlist.insetIterator(pos);
454                                 // and ensure we have a valid end iterator.
455                                 pend = par->insetlist.end();
456
457                                 if (cursor_par == par) {
458                                         // update the saved cursor position
459                                         if (cursor_pos > pos)
460                                                 --cursor_pos;
461                                 }
462                         } else {
463                                 ++pit;
464                         }
465                 }
466                 if (removed) {
467                         found = true;
468                         text->redoParagraph();
469                 }
470         }
471
472         // It is possible that the last line is empty if it was cursor_par
473         // and/or only had an error inset on it.  So we set the cursor to the
474         // start of the doc to force its removal and ensure a valid saved cursor
475         if (text->setCursor(text->ownerParagraph(), 0)
476             && 0 == cursor_par_next) {
477                 cursor_par = cursor_par_prev;
478                 cursor_pos = cursor_par->size();
479         } else if (cursor_pos > cursor_par->size()) {
480                 // Some C-Enter lines were removed by the setCursor call which
481                 // then invalidated cursor_pos. It could still be "wrong" because
482                 // the cursor may appear to have jumped but since we collapsed
483                 // some C-Enter lines this should be a reasonable compromise.
484                 cursor_pos = cursor_par->size();
485         }
486
487         // restore the original cursor in its corrected location.
488         text->setCursorIntern(cursor_par, cursor_pos);
489
490         return found;
491 }
492
493
494 void BufferView::insertErrors(TeXErrors & terr)
495 {
496         // Save the cursor position
497         LyXCursor cursor = text->cursor;
498
499         TeXErrors::Errors::const_iterator cit = terr.begin();
500         TeXErrors::Errors::const_iterator end = terr.end();
501         for (; cit != end; ++cit) {
502                 string const desctext(cit->error_desc);
503                 string const errortext(cit->error_text);
504                 string const msgtxt = desctext + '\n' + errortext;
505                 int const errorrow = cit->error_in_line;
506
507                 // Insert error string for row number
508                 int tmpid = -1;
509                 int tmppos = -1;
510
511                 if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) {
512                         buffer()->texrow.increasePos(tmpid, tmppos);
513                 }
514
515                 Paragraph * texrowpar = 0;
516
517                 if (tmpid == -1) {
518                         texrowpar = text->ownerParagraph();
519                         tmppos = 0;
520                 } else {
521                         texrowpar = buffer()->getParFromID(tmpid);
522                 }
523
524                 if (texrowpar == 0)
525                         continue;
526
527                 freezeUndo();
528                 InsetError * new_inset = new InsetError(msgtxt);
529                 text->setCursorIntern(texrowpar, tmppos);
530                 text->insertInset(new_inset);
531                 text->fullRebreak();
532                 unFreezeUndo();
533         }
534         // Restore the cursor position
535         text->setCursorIntern(cursor.par(), cursor.pos());
536 }
537
538
539 void BufferView::setCursorFromRow(int row)
540 {
541         int tmpid = -1;
542         int tmppos = -1;
543
544         buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
545
546         Paragraph * texrowpar;
547
548         if (tmpid == -1) {
549                 texrowpar = text->ownerParagraph();
550                 tmppos = 0;
551         } else {
552                 texrowpar = buffer()->getParFromID(tmpid);
553         }
554         text->setCursor(texrowpar, tmppos);
555 }
556
557
558 bool BufferView::insertInset(Inset * inset, string const & lout)
559 {
560         return pimpl_->insertInset(inset, lout);
561 }
562
563
564 void BufferView::gotoLabel(string const & label)
565 {
566         for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
567              it != buffer()->inset_iterator_end(); ++it) {
568                 vector<string> labels = it->getLabelList();
569                 if (find(labels.begin(),labels.end(),label)
570                      != labels.end()) {
571                         beforeChange(text);
572                         text->setCursor(it.getPar(), it.getPos());
573                         text->selection.cursor = text->cursor;
574                         update(text, BufferView::SELECT);
575                         return;
576                 }
577         }
578 }
579
580
581 void BufferView::undo()
582 {
583         if (!available())
584                 return;
585
586         owner()->message(_("Undo"));
587         hideCursor();
588         beforeChange(text);
589         update(text, BufferView::SELECT);
590         if (!textUndo(this))
591                 owner()->message(_("No further undo information"));
592         else
593                 update(text, BufferView::SELECT);
594         switchKeyMap();
595 }
596
597
598 void BufferView::redo()
599 {
600         if (!available())
601                 return;
602
603         owner()->message(_("Redo"));
604         hideCursor();
605         beforeChange(text);
606         update(text, BufferView::SELECT);
607         if (!textRedo(this))
608                 owner()->message(_("No further redo information"));
609         else
610                 update(text, BufferView::SELECT);
611         switchKeyMap();
612 }
613
614
615 void BufferView::copyEnvironment()
616 {
617         if (available()) {
618                 text->copyEnvironmentType();
619                 owner()->message(_("Paragraph environment type copied"));
620         }
621 }
622
623
624 void BufferView::pasteEnvironment()
625 {
626         if (available()) {
627                 text->pasteEnvironmentType();
628                 owner()->message(_("Paragraph environment type set"));
629                 update(text, BufferView::SELECT);
630         }
631 }
632
633
634 // these functions are for the spellchecker
635 WordLangTuple const BufferView::nextWord(float & value)
636 {
637         if (!available()) {
638                 value = 1;
639                 return WordLangTuple();
640         }
641
642         return text->selectNextWordToSpellcheck(value);
643 }
644
645
646 void BufferView::selectLastWord()
647 {
648         if (!available())
649                 return;
650
651         LyXCursor cur = text->selection.cursor;
652         hideCursor();
653         beforeChange(text);
654         text->selection.cursor = cur;
655         text->selectSelectedWord();
656         toggleSelection(false);
657         update(text, BufferView::SELECT);
658 }
659
660
661 void BufferView::endOfSpellCheck()
662 {
663         if (!available()) return;
664
665         hideCursor();
666         beforeChange(text);
667         text->selectSelectedWord();
668         text->clearSelection();
669         update(text, BufferView::SELECT);
670 }
671
672
673 void BufferView::replaceWord(string const & replacestring)
674 {
675         if (!available())
676                 return;
677
678         LyXText * tt = getLyXText();
679         hideCursor();
680         update(tt, BufferView::SELECT);
681
682         // clear the selection (if there is any)
683         toggleSelection(false);
684         update(tt, BufferView::SELECT);
685
686         // clear the selection (if there is any)
687         toggleSelection(false);
688         tt->replaceSelectionWithString(replacestring);
689
690         tt->setSelectionRange(replacestring.length());
691
692         // Go back so that replacement string is also spellchecked
693         for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
694                 tt->cursorLeft(this);
695         }
696         update(tt, BufferView::SELECT);
697
698         // FIXME: should be done through LFUN
699         buffer()->markDirty();
700         fitCursor();
701 }
702 // End of spellchecker stuff
703
704
705 bool BufferView::lockInset(UpdatableInset * inset)
706 {
707         if (!inset)
708                 return false;
709         // don't relock if we're already locked
710         if (theLockingInset() == inset)
711                 return true;
712         if (!theLockingInset()) {
713                 // first check if it's the inset under the cursor we want lock
714                 // should be most of the time
715                 char const c = text->cursor.par()->getChar(text->cursor.pos());
716                 if (c == Paragraph::META_INSET) {
717                         Inset * in = text->cursor.par()->getInset(text->cursor.pos());
718                         if (inset == in) {
719                                 theLockingInset(inset);
720                                 return true;
721                         }
722                 }
723                 // Then do a deep look of the inset and lock the right one
724                 int const id = inset->id();
725                 ParagraphList::iterator pit = buffer()->paragraphs.begin();
726                 ParagraphList::iterator pend = buffer()->paragraphs.end();
727                 for (; pit != pend; ++pit) {
728                         InsetList::iterator it = pit->insetlist.begin();
729                         InsetList::iterator end = pit->insetlist.end();
730                         for (; it != end; ++it) {
731                                 if (it.getInset() == inset) {
732                                         text->setCursorIntern(&*pit, it.getPos());
733                                         theLockingInset(inset);
734                                         return true;
735                                 }
736                                 if (it.getInset()->getInsetFromID(id)) {
737                                         text->setCursorIntern(&*pit, it.getPos());
738                                         it.getInset()->edit(this);
739                                         return theLockingInset()->lockInsetInInset(this, inset);
740                                 }
741                         }
742                 }
743                 return false;
744         }
745         return theLockingInset()->lockInsetInInset(this, inset);
746 }
747
748
749 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
750 {
751         if (available() && theLockingInset() && !theLockingInset()->nodraw()) {
752                 LyXCursor cursor = text->cursor;
753                 Inset * locking_inset = theLockingInset()->getLockingInset();
754
755                 if ((cursor.pos() - 1 >= 0) &&
756                     cursor.par()->isInset(cursor.pos() - 1) &&
757                     (cursor.par()->getInset(cursor.pos() - 1) ==
758                      locking_inset))
759                         text->setCursor(cursor,
760                                         cursor.par(), cursor.pos() - 1);
761                 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
762                 LyXText * txt = getLyXText();
763                 if (locking_inset->isTextInset() &&
764                     locking_inset->lyxCode() != Inset::ERT_CODE &&
765                     (txt->real_current_font.language() !=
766                      buffer()->params.language
767                      || txt->real_current_font.isVisibleRightToLeft()
768                      != buffer()->params.language->RightToLeft()))
769                         shape = (txt->real_current_font.isVisibleRightToLeft())
770                                 ? LyXScreen::REVERSED_L_SHAPE
771                                 : LyXScreen::L_SHAPE;
772                 y += cursor.iy() + theLockingInset()->insetInInsetY();
773                 screen().showManualCursor(text, x, y, asc, desc,
774                                                   shape);
775         }
776 }
777
778
779 void BufferView::hideLockedInsetCursor()
780 {
781         if (theLockingInset() && available()) {
782                 screen().hideCursor();
783         }
784 }
785
786
787 bool BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
788 {
789         if (theLockingInset() && available()) {
790                 y += text->cursor.iy() + theLockingInset()->insetInInsetY();
791                 if (screen().fitManualCursor(this, text, x, y, asc, desc)) {
792                         updateScrollbar();
793                         return true;
794                 }
795         }
796         return false;
797 }
798
799
800 int BufferView::unlockInset(UpdatableInset * inset)
801 {
802         if (!inset)
803                 return 0;
804         if (inset && theLockingInset() == inset) {
805                 inset->insetUnlock(this);
806                 theLockingInset(0);
807                 // make sure we update the combo !
808                 owner()->setLayout(getLyXText()->cursor.par()->layout()->name());
809                 // Tell the paragraph dialog that we changed paragraph
810                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
811                 finishUndo();
812                 return 0;
813         } else if (inset && theLockingInset() &&
814                    theLockingInset()->unlockInsetInInset(this, inset)) {
815                 // Tell the paragraph dialog that we changed paragraph
816                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
817                 // owner inset has updated the layout combo
818                 finishUndo();
819                 return 0;
820         }
821         return 1;
822 }
823
824
825 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
826 {
827         if (!theLockingInset())
828                 return; // shouldn't happen
829         if (kind == Undo::EDIT) // in this case insets would not be stored!
830                 kind = Undo::FINISH;
831         setUndo(this, kind,
832                 text->cursor.par(),
833                 text->cursor.par()->next());
834 }
835
836
837 void BufferView::updateInset(Inset * inset)
838 {
839         pimpl_->updateInset(inset);
840 }
841
842
843 bool BufferView::ChangeInsets(Inset::Code code,
844                               string const & from, string const & to)
845 {
846         bool need_update = false;
847         LyXCursor cursor = text->cursor;
848         LyXCursor tmpcursor = cursor;
849         cursor.par(tmpcursor.par());
850         cursor.pos(tmpcursor.pos());
851
852         ParIterator end = buffer()->par_iterator_end();
853         for (ParIterator it = buffer()->par_iterator_begin();
854              it != end; ++it) {
855                 Paragraph * par = *it;
856                 bool changed_inset = false;
857                 for (InsetList::iterator it2 = par->insetlist.begin();
858                      it2 != par->insetlist.end(); ++it2) {
859                         if (it2.getInset()->lyxCode() == code) {
860                                 InsetCommand * inset = static_cast<InsetCommand *>(it2.getInset());
861                                 if (inset->getContents() == from) {
862                                         inset->setContents(to);
863                                         changed_inset = true;
864                                 }
865                         }
866                 }
867                 if (changed_inset) {
868                         need_update = true;
869
870                         // FIXME
871
872                         // The test it.size()==1 was needed to prevent crashes.
873                         // How to set the cursor corretly when it.size()>1 ??
874                         if (it.size() == 1) {
875                                 text->setCursorIntern(par, 0);
876                                 text->redoParagraphs(text->cursor,
877                                                      text->cursor.par()->next());
878                                 text->fullRebreak();
879                         }
880                 }
881         }
882         text->setCursorIntern(cursor.par(), cursor.pos());
883         return need_update;
884 }
885
886
887 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
888 {
889         // Check if the label 'from' appears more than once
890         vector<string> labels = buffer()->getLabelList();
891
892         if (lyx::count(labels.begin(), labels.end(), from) > 1)
893                 return false;
894
895         return ChangeInsets(Inset::REF_CODE, from, to);
896 }
897
898
899 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
900 {
901         typedef pair<string, string> StringPair;
902
903         vector<StringPair> keys;
904         buffer()->fillWithBibKeys(keys);
905         if (count_if(keys.begin(), keys.end(),
906                      lyx::equal_1st_in_pair<StringPair>(from))
907             > 1)
908                 return false;
909
910         return ChangeInsets(Inset::CITE_CODE, from, to);
911 }
912
913
914 UpdatableInset * BufferView::theLockingInset() const
915 {
916         // If NULL is not allowed we should put an Assert here. (Lgb)
917         if (text)
918                 return text->the_locking_inset;
919         return 0;
920 }
921
922
923 void BufferView::theLockingInset(UpdatableInset * inset)
924 {
925         text->the_locking_inset = inset;
926 }
927
928
929 LyXText * BufferView::getLyXText() const
930 {
931         if (theLockingInset()) {
932                 LyXText * txt = theLockingInset()->getLyXText(this, true);
933                 if (txt)
934                         return txt;
935         }
936         return text;
937 }
938
939
940 LyXText * BufferView::getParentText(Inset * inset) const
941 {
942         if (inset->owner()) {
943                 LyXText * txt = inset->getLyXText(this);
944                 inset = inset->owner();
945                 while (inset && inset->getLyXText(this) == txt)
946                         inset = inset->owner();
947                 if (inset)
948                         return inset->getLyXText(this);
949         }
950         return text;
951 }
952
953
954 Language const * BufferView::getParentLanguage(Inset * inset) const
955 {
956         LyXText * text = getParentText(inset);
957         return text->cursor.par()->getFontSettings(buffer()->params,
958                                                    text->cursor.pos()).language();
959 }
960
961
962 Encoding const * BufferView::getEncoding() const
963 {
964         LyXText * t = getLyXText();
965         if (!t)
966                 return 0;
967
968         LyXCursor const & c= t->cursor;
969         LyXFont const font = c.par()->getFont(buffer()->params, c.pos());
970         return font.language()->encoding();
971 }
972
973
974 void BufferView::haveSelection(bool sel)
975 {
976         pimpl_->workarea().haveSelection(sel);
977 }
978
979
980 int BufferView::workHeight() const
981 {
982         return pimpl_->workarea().workHeight();
983 }