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