]> git.lyx.org Git - lyx.git/blob - src/BufferView2.C
Lots of fixes for text/tabular insets. Now tabular insets work quite good
[lyx.git] / src / BufferView2.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ====================================================== 
4  * 
5  *           LyX, The Document Processor
6  *        
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-2000 The LyX Team.
9  *
10  * ====================================================== */
11
12 #include <config.h>
13
14 #include <fstream>
15
16 #include "BufferView.h"
17 #include "buffer.h"
18 #include "lyxcursor.h"
19 #include "lyxtext.h"
20 #include "insets/inseterror.h"
21 #include "insets/insetinfo.h"
22 #include "insets/insetspecialchar.h"
23 #include "LyXView.h"
24 #include "minibuffer.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"
30 #include "LaTeX.h"
31 #include "BufferView_pimpl.h"
32
33 extern BufferList bufferlist;
34
35 using std::pair;
36 using std::endl;
37 using std::ifstream;
38
39 // Inserts a file into current document
40 bool BufferView::insertLyXFile(string const & filen)
41         //
42         // Copyright CHT Software Service GmbH
43         // Uwe C. Schroeder
44         //
45         // Insert a Lyxformat - file into current buffer
46         //
47         // Moved from lyx_cb.C (Lgb)
48 {
49         if (filen.empty()) return false;
50
51         string fname = MakeAbsPath(filen);
52
53         // check if file exist
54         FileInfo fi(fname);
55
56         if (!fi.readable()) {
57                 WriteAlert(_("Error!"),
58                            _("Specified file is unreadable: "),
59                            MakeDisplayPath(fname, 50));
60                 return false;
61         }
62         
63         beforeChange();
64
65         ifstream ifs(fname.c_str());
66         if (!ifs) {
67                 WriteAlert(_("Error!"),
68                            _("Cannot open specified file: "),
69                            MakeDisplayPath(fname, 50));
70                 return false;
71         }
72         LyXLex lex(0, 0);
73         lex.setStream(ifs);
74         char c; ifs.get(c);
75         ifs.putback(c);
76
77         bool res = true;
78
79         if (c == '#') {
80                 lyxerr.debug() << "Will insert file with header" << endl;
81                 res = buffer()->readFile(lex, text->cursor.par);
82         } else {
83                 lyxerr.debug() << "Will insert file without header" << endl;
84                 res = buffer()->readLyXformat2(lex, text->cursor.par);
85         }
86
87         resize();
88         return res;
89 }
90
91 bool BufferView::removeAutoInsets()
92 {
93         LyXParagraph * par = buffer()->paragraph;
94
95         LyXCursor cursor = text->cursor;
96         LyXCursor tmpcursor = cursor;
97         cursor.par = tmpcursor.par->ParFromPos(tmpcursor.pos);
98         cursor.pos = tmpcursor.par->PositionInParFromPos(tmpcursor.pos);
99
100         bool a = false;
101         while (par) {
102                 if (par->AutoDeleteInsets()){
103                         a = true;
104                         if (par->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE){
105                                 // this is possible now, since SetCursor takes
106                                 // care about footnotes
107                                 text->SetCursorIntern(par, 0);
108                                 text->RedoParagraphs(text->cursor,
109                                                      text->cursor.par->Next());
110                                 text->FullRebreak();
111                         }
112                 }
113                 par = par->next;
114         }
115         // avoid forbidden cursor positions caused by error removing
116         if (cursor.pos > cursor.par->Last())
117                 cursor.pos = cursor.par->Last();
118         text->SetCursorIntern(cursor.par, cursor.pos);
119
120         return a;
121 }
122
123
124 void BufferView::insertErrors(TeXErrors & terr)
125 {
126         // Save the cursor position
127         LyXCursor cursor = text->cursor;
128
129         // This is drastic, but it's the only fix, I could find. (Asger)
130         allFloats(1, 0);
131         allFloats(1, 1);
132
133         for (TeXErrors::Errors::const_iterator cit = terr.begin();
134              cit != terr.end();
135              ++cit) {
136                 string desctext((*cit).error_desc);
137                 string errortext((*cit).error_text);
138                 string msgtxt = desctext + '\n' + errortext;
139                 int errorrow = (*cit).error_in_line;
140
141                 // Insert error string for row number
142                 int tmpid = -1; 
143                 int tmppos = -1;
144
145                 buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos);
146
147                 LyXParagraph * texrowpar = 0;
148
149                 if (tmpid == -1) {
150                         texrowpar = text->FirstParagraph();
151                         tmppos = 0;
152                 } else {
153                         texrowpar = text->GetParFromID(tmpid);
154                 }
155
156                 if (texrowpar == 0)
157                         continue;
158
159                 InsetError * new_inset = new InsetError(msgtxt);
160                 text->SetCursorIntern(texrowpar, tmppos);
161                 text->InsertInset(new_inset);
162                 text->FullRebreak();
163         }
164         // Restore the cursor position
165         text->SetCursorIntern(cursor.par, cursor.pos);
166 }
167
168
169 void BufferView::setCursorFromRow(int row)
170 {
171         int tmpid = -1; 
172         int tmppos = -1;
173
174         buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
175
176         LyXParagraph * texrowpar;
177
178         if (tmpid == -1) {
179                 texrowpar = text->FirstParagraph();
180                 tmppos = 0;
181         } else {
182                 texrowpar = text->GetParFromID(tmpid);
183         }
184         text->SetCursor(texrowpar, tmppos);
185 }
186
187 bool BufferView::insertInset(Inset * inset, string const & lout,
188                          bool no_table)
189 {
190         // if we are in a locking inset we should try to insert the
191         // inset there otherwise this is a illegal function now
192         if (the_locking_inset) {
193                 if (the_locking_inset->InsertInsetAllowed(inset) &&
194                     the_locking_inset->InsertInset(this, inset))
195                         return true;
196                 return false;
197         }
198
199         // check for table/list in tables
200         if (no_table && text->cursor.par->table){
201                 WriteAlert(_("Impossible Operation!"),
202                            _("Cannot insert table/list in table."),
203                            _("Sorry."));
204                 return false;
205         }
206
207         // not quite sure if we want this...
208         text->SetCursorParUndo();
209         text->FreezeUndo();
210         
211         beforeChange();
212         if (!lout.empty()) {
213                 update(-2);
214                 text->BreakParagraph();
215                 update(-1);
216                 
217                 if (text->cursor.par->Last()) {
218                         text->CursorLeft();
219                         
220                         text->BreakParagraph();
221                         update(-1);
222                 }
223
224                 pair<bool, LyXTextClass::size_type> lres =
225                         textclasslist.NumberOfLayout(buffer()->params
226                                                      .textclass, lout);
227                 LyXTextClass::size_type lay;
228                 if (lres.first != false) {
229                         // layout found
230                         lay = lres.second;
231                 } else {
232                         // layout not fount using default "Standard" (0)
233                         lay = 0;
234                 }
235                  
236                 text->SetLayout(lay);
237                 
238                 text->SetParagraph(0, 0,
239                                    0, 0,
240                                    VSpace(VSpace::NONE), VSpace(VSpace::NONE),
241                                    LYX_ALIGN_LAYOUT, 
242                                    string(),
243                                    0);
244                 update(-1);
245                 
246                 text->current_font.setLatex(LyXFont::OFF);
247         }
248         
249         text->InsertInset(inset);
250 #if 1
251         // if we enter a text-inset the cursor should be to the left side
252         // of it! This couldn't happen before as Undo was not handled inside
253         // inset now after the Undo LyX tries to call inset->Edit(...) again
254         // and cannot do this as the cursor is behind the inset and GetInset
255         // does not return the inset!
256         if (inset->IsTextInset()) {
257                 if (text->cursor.par->isRightToLeftPar())
258                         text->CursorRight();
259                 else
260                         text->CursorLeft();
261         }
262 #endif
263         update(-1);
264
265         text->UnFreezeUndo();
266         return true;
267 }
268
269
270 // Open and lock an updatable inset
271 void BufferView::open_new_inset(UpdatableInset * new_inset)
272 {
273         beforeChange();
274         text->FinishUndo();
275         insertInset(new_inset);
276         text->CursorLeft();
277         update(1);
278         new_inset->Edit(this, 0, 0, 0);
279 }
280
281 /* This is also a buffer property (ale) */
282 // Not so sure about that. a goto Label function can not be buffer local, just
283 // think how this will work in a multiwindo/buffer environment, all the
284 // cursors in all the views showing this buffer will move. (Lgb)
285 // OK, then no cursor action should be allowed in buffer. (ale)
286 bool BufferView::gotoLabel(string const & label)
287
288 {
289         LyXParagraph * par = buffer()->paragraph;
290         LyXParagraph::size_type pos;
291         Inset * inset;
292         while (par) {
293                 pos = -1;
294                 while ((inset = par->ReturnNextInsetPointer(pos))){     
295                         for (int i = 0; i < inset->GetNumberOfLabels(); ++i) {
296                                 if (label == inset->getLabel(i)) {
297                                         beforeChange();
298                                         text->SetCursor(par, pos);
299                                         text->sel_cursor = text->cursor;
300                                         update(0);
301                                         return true;
302                                 }
303                         }
304                         ++pos;
305                 } 
306                 par = par->next;
307         }
308         return false;
309 }
310
311 void BufferView::allFloats(char flag, char figmar)
312 {
313         if (!available()) return;
314
315         LyXCursor cursor = text->cursor;
316
317         if (!flag && cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE
318             && ((figmar 
319                  && cursor.par->footnotekind != LyXParagraph::FOOTNOTE 
320                  && cursor.par->footnotekind != LyXParagraph::MARGIN)
321                 || (!figmar
322                     && cursor.par->footnotekind != LyXParagraph::FIG 
323                     && cursor.par->footnotekind != LyXParagraph::TAB
324                     && cursor.par->footnotekind != LyXParagraph::WIDE_FIG 
325                     && cursor.par->footnotekind != LyXParagraph::WIDE_TAB
326                     && cursor.par->footnotekind != LyXParagraph::ALGORITHM)))
327                 toggleFloat();
328         else
329                 beforeChange();
330
331         LyXCursor tmpcursor = cursor;
332         cursor.par = tmpcursor.par->ParFromPos(tmpcursor.pos);
333         cursor.pos = tmpcursor.par->PositionInParFromPos(tmpcursor.pos);
334
335         LyXParagraph *par = buffer()->paragraph;
336         while (par) {
337                 if (flag) {
338                         if (par->footnoteflag == LyXParagraph::CLOSED_FOOTNOTE
339                             && ((figmar 
340                                  && par->footnotekind != LyXParagraph::FOOTNOTE 
341                                  && par->footnotekind !=  LyXParagraph::MARGIN)
342                                 || (!figmar
343                                     && par->footnotekind != LyXParagraph::FIG 
344                                     && par->footnotekind != LyXParagraph::TAB
345                                     && par->footnotekind != LyXParagraph::WIDE_FIG 
346                                     && par->footnotekind != LyXParagraph::WIDE_TAB
347                                     && par->footnotekind != LyXParagraph::ALGORITHM
348                                         )
349                                     )
350                                 ) {
351                                 if (par->previous
352                                     && par->previous->footnoteflag != 
353                                     LyXParagraph::CLOSED_FOOTNOTE){ /* should be */ 
354                                         text->SetCursorIntern(par->previous,
355                                                               0);
356                                         text->OpenFootnote();
357                                 }
358                         }
359                 } else {
360                         if (par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE
361                             && (
362                                     (figmar 
363                                      &&
364                                      par->footnotekind != LyXParagraph::FOOTNOTE 
365                                      &&
366                                      par->footnotekind !=  LyXParagraph::MARGIN
367                                             )
368                                     ||
369                                     (!figmar
370                                      &&
371                                      par->footnotekind != LyXParagraph::FIG 
372                                      &&
373                                      par->footnotekind != LyXParagraph::TAB
374                                      &&
375                                      par->footnotekind != LyXParagraph::WIDE_FIG 
376                                      &&
377                                      par->footnotekind != LyXParagraph::WIDE_TAB
378                                      &&
379                                      par->footnotekind != LyXParagraph::ALGORITHM
380                                             )
381                                     )
382                                 ) {
383                                 text->SetCursorIntern(par, 0);
384                                 text->CloseFootnote();
385                         }
386                 }
387                 par = par->next;
388         }
389
390         text->SetCursorIntern(cursor.par, cursor.pos);
391         redraw();
392         fitCursor();
393         //updateScrollbar();
394 }
395
396
397 void BufferView::insertNote()
398 {
399         InsetInfo * new_inset = new InsetInfo();
400         insertInset(new_inset);
401         new_inset->Edit(this, 0, 0, 0);
402 }
403
404
405 void BufferView::openStuff()
406 {
407         if (available()) {
408                 owner()->getMiniBuffer()->Set(_("Open/Close..."));
409                 hideCursor();
410                 beforeChange();
411                 update(-2);
412                 text->OpenStuff();
413                 update(0);
414                 setState();
415         }
416 }
417
418
419 void BufferView::toggleFloat()
420 {
421         if (available()) {
422                 owner()->getMiniBuffer()->Set(_("Open/Close..."));
423                 hideCursor();
424                 beforeChange();
425                 update(-2);
426                 text->ToggleFootnote();
427                 update(0);
428                 setState();
429         }
430 }
431
432 void BufferView::menuUndo()
433 {
434         if (available()) {
435                 owner()->getMiniBuffer()->Set(_("Undo"));
436                 hideCursor();
437                 beforeChange();
438                 update(-2);
439                 if (!text->TextUndo())
440                         owner()->getMiniBuffer()->Set(_("No further undo information"));
441                 else
442                         update(-1);
443                 setState();
444         }
445 }
446
447
448 void BufferView::menuRedo()
449 {
450         if (the_locking_inset) {
451                 owner()->getMiniBuffer()->Set(_("Redo not yet supported in math mode"));
452                 return;
453         }    
454    
455         if (available()) {
456                 owner()->getMiniBuffer()->Set(_("Redo"));
457                 hideCursor();
458                 beforeChange();
459                 update(-2);
460                 if (!text->TextRedo())
461                         owner()->getMiniBuffer()->Set(_("No further redo information"));
462                 else
463                         update(-1);
464                 setState();
465         }
466 }
467
468
469 void BufferView::hyphenationPoint()
470 {
471         if (available()) {
472                 hideCursor();
473                 update(-2);
474                 InsetSpecialChar * new_inset = 
475                         new InsetSpecialChar(InsetSpecialChar::HYPHENATION);
476                 insertInset(new_inset);
477         }
478 }
479
480
481 void BufferView::ldots()
482 {
483         if (available())  {
484                 hideCursor();
485                 update(-2);
486                 InsetSpecialChar * new_inset = 
487                         new InsetSpecialChar(InsetSpecialChar::LDOTS);
488                 insertInset(new_inset);
489         }
490 }
491
492
493 void BufferView::endOfSentenceDot()
494 {
495         if (available()) {
496                 hideCursor();
497                 update(-2);
498                 InsetSpecialChar * new_inset = 
499                         new InsetSpecialChar(InsetSpecialChar::END_OF_SENTENCE);
500                 insertInset(new_inset);
501         }
502 }
503
504
505 void BufferView::menuSeparator()
506 {
507         if (available()) {
508                 hideCursor();
509                 update(-2);
510                 InsetSpecialChar * new_inset = 
511                         new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR);
512                 insertInset(new_inset);
513         }
514 }
515
516
517 void BufferView::newline()
518 {
519         if (available()) {
520                 hideCursor();
521                 update(-2);
522                 text->InsertChar(LyXParagraph::META_NEWLINE);
523                 update(-1);
524         }
525 }
526
527
528 void BufferView::protectedBlank()
529 {
530         if (available()) {
531                 hideCursor();
532                 update(-2);
533                 InsetSpecialChar * new_inset =
534                         new InsetSpecialChar(InsetSpecialChar::PROTECTED_SEPARATOR);
535                 insertInset(new_inset);
536         }
537 }
538
539
540 void BufferView::hfill()
541 {
542         if (available()) {
543                 hideCursor();
544                 update(-2);
545                 text->InsertChar(LyXParagraph::META_HFILL);
546                 update(-1);
547         }
548 }
549
550 void BufferView::copyEnvironment()
551 {
552         if (available()) {
553                 text->copyEnvironmentType();
554                 // clear the selection, even if mark_set
555                 toggleSelection();
556                 text->ClearSelection();
557                 update(-2);
558                 owner()->getMiniBuffer()->Set(_("Paragraph environment type copied"));
559         }
560 }
561
562
563 void BufferView::pasteEnvironment()
564 {
565         if (available()) {
566                 text->pasteEnvironmentType();
567                 owner()->getMiniBuffer()->Set(_("Paragraph environment type set"));
568                 update(1);
569         }
570 }
571
572
573 void BufferView::copy()
574 {
575         if (available()) {
576                 text->CopySelection();
577                 // clear the selection, even if mark_set
578                 toggleSelection();
579                 text->ClearSelection();
580                 update(-2);
581                 owner()->getMiniBuffer()->Set(_("Copy"));
582         }
583 }
584
585 void BufferView::cut()
586 {
587         if (available()) {
588                 hideCursor();
589                 update(-2);
590                 text->CutSelection();
591                 update(1);
592                 owner()->getMiniBuffer()->Set(_("Cut"));
593         }
594 }
595
596
597 void BufferView::paste()
598 {
599         if (!available()) return;
600         
601         owner()->getMiniBuffer()->Set(_("Paste"));
602         hideCursor();
603         // clear the selection
604         toggleSelection();
605         text->ClearSelection();
606         update(-2);
607         
608         // paste
609         text->PasteSelection();
610         update(1);
611         
612         // clear the selection 
613         toggleSelection();
614         text->ClearSelection();
615         update(-2);
616 }
617
618
619 void BufferView::gotoNote()
620 {
621         if (!available()) return;
622    
623         hideCursor();
624         beforeChange();
625         update(-2);
626         LyXCursor tmp;
627    
628         if (!text->GotoNextNote()) {
629                 if (text->cursor.pos 
630                     || text->cursor.par != text->FirstParagraph()) {
631                                 tmp = text->cursor;
632                                 text->cursor.par = text->FirstParagraph();
633                                 text->cursor.pos = 0;
634                                 if (!text->GotoNextNote()) {
635                                         text->cursor = tmp;
636                                         owner()->getMiniBuffer()->Set(_("No more notes"));
637                                         LyXBell();
638                                 }
639                         } else {
640                                 owner()->getMiniBuffer()->Set(_("No more notes"));
641                                 LyXBell();
642                         }
643         }
644         update(0);
645         text->sel_cursor = text->cursor;
646 }
647
648
649 void BufferView::insertCorrectQuote()
650 {
651         char c;
652
653         if (text->cursor.pos)
654                 c = text->cursor.par->GetChar(text->cursor.pos - 1);
655         else 
656                 c = ' ';
657
658         insertInset(new InsetQuotes(c, buffer()->params));
659 }
660
661
662 /* these functions are for the spellchecker */ 
663 char * BufferView::nextWord(float & value)
664 {
665         if (!available()) {
666                 value = 1;
667                 return 0;
668         }
669
670         char * string = text->SelectNextWord(value);
671
672         return string;
673 }
674
675   
676 void BufferView::selectLastWord()
677 {
678         if (!available()) return;
679    
680         hideCursor();
681         beforeChange();
682         text->SelectSelectedWord();
683         toggleSelection(false);
684         update(0);
685 }
686
687
688 void BufferView::endOfSpellCheck()
689 {
690         if (!available()) return;
691    
692         hideCursor();
693         beforeChange();
694         text->SelectSelectedWord();
695         text->ClearSelection();
696         update(0);
697 }
698
699
700 void BufferView::replaceWord(string const & replacestring)
701 {
702         if (!available()) return;
703
704         hideCursor();
705         update(-2);
706    
707         /* clear the selection (if there is any) */ 
708         toggleSelection(false);
709         update(-2);
710    
711         /* clear the selection (if there is any) */ 
712         toggleSelection(false);
713         text->ReplaceSelectionWithString(replacestring.c_str());
714    
715         text->SetSelectionOverString(replacestring.c_str());
716
717         // Go back so that replacement string is also spellchecked
718         for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
719                 text->CursorLeftIntern();
720         }
721         update(1);
722 }
723 // End of spellchecker stuff
724
725
726 bool BufferView::lockInset(UpdatableInset * inset)
727 {
728         if (!the_locking_inset && inset) {
729                 the_locking_inset = inset;
730                 return true;
731         } else if (inset) {
732             return the_locking_inset->LockInsetInInset(this, inset);
733         }
734         return false;
735 }
736
737
738 void BufferView::showLockedInsetCursor(long x, long y, int asc, int desc)
739 {
740         if (the_locking_inset && available()) {
741                 LyXCursor cursor = text->cursor;
742                 if ((cursor.pos - 1 >= 0) &&
743                     (cursor.par->GetChar(cursor.pos-1) ==
744                      LyXParagraph::META_INSET) &&
745                     (cursor.par->GetInset(cursor.pos - 1) ==
746                      the_locking_inset->GetLockingInset()))
747                         text->SetCursor(cursor, cursor.par, cursor.pos-1);
748                 y += cursor.y + the_locking_inset->InsetInInsetY();
749                 pimpl_->screen->ShowManualCursor(x, y, asc, desc,
750                                          LyXScreen::BAR_SHAPE);
751         }
752 }
753
754
755 void BufferView::hideLockedInsetCursor()
756 {
757         if (the_locking_inset && available()) {
758                 pimpl_->screen->HideCursor();
759         }
760 }
761
762
763 void BufferView::fitLockedInsetCursor(long x, long y, int asc, int desc)
764 {
765         if (the_locking_inset && available()){
766                 y += text->cursor.y + the_locking_inset->InsetInInsetY();
767                 if (pimpl_->screen->FitManualCursor(x, y, asc, desc))
768                         updateScrollbar();
769         }
770 }
771
772
773 int BufferView::unlockInset(UpdatableInset * inset)
774 {
775         if (inset && the_locking_inset == inset) {
776                 inset->InsetUnlock(this);
777                 the_locking_inset = 0;
778                 text->FinishUndo();
779                 return 0;
780         } else if (inset && the_locking_inset &&
781                    the_locking_inset->UnlockInsetInInset(this, inset)) {
782                 text->FinishUndo();
783                 return 0;
784         }
785         return bufferlist.unlockInset(inset);
786 }
787
788
789 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
790 {
791         if (!the_locking_inset)
792                 return; // shouldn't happen
793         if (kind == Undo::EDIT) // in this case insets would not be stored!
794                 kind = Undo::FINISH;
795         text->SetUndo(kind,
796                       text->cursor.par->
797                       ParFromPos(text->cursor.pos)->previous, 
798                       text->cursor.par->
799                       ParFromPos(text->cursor.pos)->next);
800 }
801
802
803 void BufferView::updateInset(Inset * inset, bool mark_dirty)
804 {
805         if (!inset)
806                 return;
807
808         // first check for locking insets
809         if (the_locking_inset) {
810                 if (the_locking_inset == inset) {
811                         if (text->UpdateInset(inset)){
812                                 update();
813                                 if (mark_dirty){
814                                         if (buffer()->isLyxClean())
815                                                 owner()->getMiniBuffer()->
816                                                         setTimer(4);
817                                         buffer()->markDirty();
818                                 }
819                                 updateScrollbar();
820                                 return;
821                         }
822                 } else if (the_locking_inset->UpdateInsetInInset(this,inset)) {
823                         if (text->UpdateInset(the_locking_inset)) {
824                                 update();
825                                 if (mark_dirty){
826                                         if (buffer()->isLyxClean())
827                                                 owner()->getMiniBuffer()->
828                                                         setTimer(4);
829                                         buffer()->markDirty();
830                                 }
831                                 updateScrollbar();
832                                 return;
833                         }
834                 }
835         }
836   
837         // then check the current buffer
838         if (available()) {
839                 hideCursor();
840                 update(-3);
841                 if (text->UpdateInset(inset)){
842                         if (mark_dirty)
843                                 update(1);
844                         else 
845                                 update(3);
846                         return;
847                 }
848         }
849 }