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