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