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