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