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