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