]> git.lyx.org Git - lyx.git/blob - src/BufferView2.C
re-enable automatic deletion of empty super/subscripts;
[lyx.git] / src / BufferView2.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *        
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include "BufferView.h"
14 #include "buffer.h"
15 #include "lyxcursor.h"
16 #include "lyxtext.h"
17 #include "LyXView.h"
18 #include "bufferlist.h"
19 #include "lyxscreen.h"
20 #include "LaTeX.h"
21 #include "BufferView_pimpl.h"
22 #include "language.h"
23 #include "gettext.h"
24 #include "undo_funcs.h"
25 #include "debug.h"
26 #include "iterators.h"
27
28 #include "frontends/Alert.h"
29
30 #include "insets/insetcommand.h" //ChangeRefs
31 #include "insets/inseterror.h"
32
33 #include "support/FileInfo.h"
34 #include "support/filetools.h"
35 #include "support/lyxfunctional.h" //equal_1st_in_pair
36
37 #include <fstream>
38 #include <algorithm>
39
40 extern BufferList bufferlist;
41
42 using std::pair;
43 using std::endl;
44 using std::ifstream;
45 using std::vector;
46 using std::find;
47 using std::count;
48 using std::count_if;
49
50
51 // Inserts a file into current document
52 bool BufferView::insertLyXFile(string const & filen)
53         //
54         // Copyright CHT Software Service GmbH
55         // Uwe C. Schroeder
56         //
57         // Insert a Lyxformat - file into current buffer
58         //
59         // Moved from lyx_cb.C (Lgb)
60 {
61         if (filen.empty()) return false;
62
63         string const fname = MakeAbsPath(filen);
64
65         // check if file exist
66         FileInfo const fi(fname);
67
68         if (!fi.readable()) {
69                 Alert::alert(_("Error!"),
70                            _("Specified file is unreadable: "),
71                            MakeDisplayPath(fname, 50));
72                 return false;
73         }
74         
75         beforeChange(text);
76
77         ifstream ifs(fname.c_str());
78         if (!ifs) {
79                 Alert::alert(_("Error!"),
80                            _("Cannot open specified file: "),
81                            MakeDisplayPath(fname, 50));
82                 return false;
83         }
84         
85         int const c = ifs.peek();
86        
87         LyXLex lex(0, 0);
88         lex.setStream(ifs);
89
90         bool res = true;
91
92         if (c == '#') {
93                 lyxerr[Debug::INFO] << "Will insert file with header" << endl;
94                 res = buffer()->readFile(lex, text->cursor.par());
95         } else {
96                 lyxerr[Debug::INFO] << "Will insert file without header" 
97                                     << endl;
98                 res = buffer()->readLyXformat2(lex, text->cursor.par());
99         }
100
101         resize();
102         return res;
103 }
104
105
106 bool BufferView::removeAutoInsets()
107 {
108         LyXCursor tmpcursor = text->cursor;
109         LyXCursor cursor;
110         bool found = false;
111
112         ParIterator end = buffer()->par_iterator_end();
113         for (ParIterator it = buffer()->par_iterator_begin();
114              it != end; ++it) {
115                 Paragraph * par = *it;
116                 // this has to be done before the delete
117                 if (par->autoDeleteInsets()) {
118                         found = true;
119 #ifdef WITH_WARNINGS
120 #warning FIXME
121 #endif
122                         // The test it.size()==1 was needed to prevent crashes.
123                         if (it.size() == 1) {
124                                 text->setCursor(this, cursor, par, 0);
125                                 text->redoParagraphs(this, cursor,
126                                                      cursor.par()->next());
127                                 text->fullRebreak(this);
128                         }
129                 }
130         }
131
132         // avoid forbidden cursor positions caused by error removing
133         if (tmpcursor.pos() > tmpcursor.par()->size())
134                 tmpcursor.pos(tmpcursor.par()->size());
135
136         text->setCursorIntern(this, tmpcursor.par(), tmpcursor.pos());
137
138         return found;
139 }
140
141
142 void BufferView::insertErrors(TeXErrors & terr)
143 {
144         // Save the cursor position
145         LyXCursor cursor = text->cursor;
146
147         for (TeXErrors::Errors::const_iterator cit = terr.begin();
148              cit != terr.end();
149              ++cit) {
150                 string const desctext(cit->error_desc);
151                 string const errortext(cit->error_text);
152                 string const msgtxt = desctext + '\n' + errortext;
153                 int const errorrow = cit->error_in_line;
154
155                 // Insert error string for row number
156                 int tmpid = -1; 
157                 int tmppos = -1;
158
159                 if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) {
160                         buffer()->texrow.increasePos(tmpid, tmppos);
161                 }
162                 
163                 Paragraph * texrowpar = 0;
164
165                 if (tmpid == -1) {
166                         texrowpar = text->firstParagraph();
167                         tmppos = 0;
168                 } else {
169                         texrowpar = buffer()->getParFromID(tmpid);
170                 }
171
172                 if (texrowpar == 0)
173                         continue;
174
175                 InsetError * new_inset = new InsetError(msgtxt);
176                 text->setCursorIntern(this, texrowpar, tmppos);
177                 text->insertInset(this, new_inset);
178                 text->fullRebreak(this);
179         }
180         // Restore the cursor position
181         text->setCursorIntern(this, cursor.par(), cursor.pos());
182 }
183
184
185 void BufferView::setCursorFromRow(int row)
186 {
187         int tmpid = -1; 
188         int tmppos = -1;
189
190         buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
191
192         Paragraph * texrowpar;
193
194         if (tmpid == -1) {
195                 texrowpar = text->firstParagraph();
196                 tmppos = 0;
197         } else {
198                 texrowpar = buffer()->getParFromID(tmpid);
199         }
200         text->setCursor(this, texrowpar, tmppos);
201 }
202
203
204 bool BufferView::insertInset(Inset * inset, string const & lout)
205 {
206         return pimpl_->insertInset(inset, lout);
207 }
208
209
210 /* This is also a buffer property (ale) */
211 // Not so sure about that. a goto Label function can not be buffer local, just
212 // think how this will work in a multiwindo/buffer environment, all the
213 // cursors in all the views showing this buffer will move. (Lgb)
214 // OK, then no cursor action should be allowed in buffer. (ale)
215 bool BufferView::gotoLabel(string const & label)
216
217 {
218         for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
219              it != buffer()->inset_iterator_end(); ++it) {
220                 vector<string> labels = (*it)->getLabelList();
221                 if (find(labels.begin(),labels.end(),label)
222                      != labels.end()) {
223                         beforeChange(text);
224                         text->setCursor(this, it.getPar(), it.getPos());
225                         text->selection.cursor = text->cursor;
226                         update(text, BufferView::SELECT|BufferView::FITCUR);
227                         return true;
228                 }
229         }
230         return false;
231 }
232
233
234 void BufferView::menuUndo()
235 {
236         if (available()) {
237                 owner()->message(_("Undo"));
238                 hideCursor();
239                 beforeChange(text);
240                 update(text, BufferView::SELECT|BufferView::FITCUR);
241                 if (!textUndo(this))
242                         owner()->message(_("No further undo information"));
243                 else
244                         update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
245                 setState();
246         }
247 }
248
249
250 void BufferView::menuRedo()
251 {
252         if (theLockingInset()) {
253                 owner()->message(_("Redo not yet supported in math mode"));
254                 return;
255         }    
256    
257         if (available()) {
258                 owner()->message(_("Redo"));
259                 hideCursor();
260                 beforeChange(text);
261                 update(text, BufferView::SELECT|BufferView::FITCUR);
262                 if (!textRedo(this))
263                         owner()->message(_("No further redo information"));
264                 else
265                         update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
266                 setState();
267         }
268 }
269
270
271 void BufferView::copyEnvironment()
272 {
273         if (available()) {
274                 text->copyEnvironmentType();
275                 owner()->message(_("Paragraph environment type copied"));
276         }
277 }
278
279
280 void BufferView::pasteEnvironment()
281 {
282         if (available()) {
283                 text->pasteEnvironmentType(this);
284                 owner()->message(_("Paragraph environment type set"));
285                 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
286         }
287 }
288
289
290 void BufferView::copy()
291 {
292         if (available()) {
293                 text->copySelection(this);
294                 owner()->message(_("Copy"));
295         }
296 }
297
298
299 void BufferView::cut(bool realcut)
300 {
301         if (available()) {
302                 hideCursor();
303                 update(text, BufferView::SELECT|BufferView::FITCUR);
304                 text->cutSelection(this, true, realcut);
305                 update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
306                 owner()->message(_("Cut"));
307         }
308 }
309
310
311 void BufferView::paste()
312 {
313         if (!available()) return;
314
315         owner()->message(_("Paste"));
316
317         hideCursor();
318         // clear the selection
319         toggleSelection();
320         text->clearSelection();
321         update(text, BufferView::SELECT|BufferView::FITCUR);
322         
323         // paste
324         text->pasteSelection(this);
325         update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
326         
327         // clear the selection 
328         toggleSelection();
329         text->clearSelection();
330         update(text, BufferView::SELECT|BufferView::FITCUR);
331 }
332
333
334 /* these functions are for the spellchecker */ 
335 string const BufferView::nextWord(float & value)
336 {
337         if (!available()) {
338                 value = 1;
339                 return string();
340         }
341
342         return text->selectNextWordToSpellcheck(this, value);
343 }
344
345   
346 void BufferView::selectLastWord()
347 {
348         if (!available()) return;
349    
350         LyXCursor cur = text->selection.cursor;
351         hideCursor();
352         beforeChange(text);
353         text->selection.cursor = cur;
354         text->selectSelectedWord(this);
355         toggleSelection(false);
356         update(text, BufferView::SELECT|BufferView::FITCUR);
357 }
358
359
360 void BufferView::endOfSpellCheck()
361 {
362         if (!available()) return;
363    
364         hideCursor();
365         beforeChange(text);
366         text->selectSelectedWord(this);
367         text->clearSelection();
368         update(text, BufferView::SELECT|BufferView::FITCUR);
369 }
370
371
372 void BufferView::replaceWord(string const & replacestring)
373 {
374         if (!available()) return;
375
376         LyXText * tt = getLyXText();
377         hideCursor();
378         update(tt, BufferView::SELECT|BufferView::FITCUR);
379    
380         /* clear the selection (if there is any) */ 
381         toggleSelection(false);
382         update(tt, BufferView::SELECT|BufferView::FITCUR);
383    
384         /* clear the selection (if there is any) */ 
385         toggleSelection(false);
386         tt->replaceSelectionWithString(this, replacestring);
387    
388         tt->setSelectionOverString(this, replacestring);
389
390         // Go back so that replacement string is also spellchecked
391         for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
392                 tt->cursorLeft(this);
393         }
394         update(tt, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
395 }
396 // End of spellchecker stuff
397
398
399 bool BufferView::lockInset(UpdatableInset * inset)
400 {
401         // don't relock if we're already locked
402         if (theLockingInset() == inset)
403                 return true;
404         if (!theLockingInset() && inset) {
405                 theLockingInset(inset);
406                 return true;
407         } else if (inset) {
408             return theLockingInset()->lockInsetInInset(this, inset);
409         }
410         return false;
411 }
412
413
414 void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc)
415 {
416         if (available() && theLockingInset()) {
417                 LyXCursor cursor = text->cursor;
418                 Inset * locking_inset = theLockingInset()->getLockingInset();
419
420                 if ((cursor.pos() - 1 >= 0) &&
421                     cursor.par()->isInset(cursor.pos() - 1) &&
422                     (cursor.par()->getInset(cursor.pos() - 1) ==
423                      locking_inset))
424                         text->setCursor(this, cursor,
425                                         cursor.par(), cursor.pos() - 1);
426                 LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE;
427                 LyXText * txt = getLyXText();
428                 if (locking_inset->isTextInset() &&
429                     locking_inset->lyxCode() != Inset::ERT_CODE &&
430                     (txt->real_current_font.language() !=
431                      buffer()->params.language
432                      || txt->real_current_font.isVisibleRightToLeft()
433                      != buffer()->params.language->RightToLeft()))
434                         shape = (txt->real_current_font.isVisibleRightToLeft())
435                                 ? LyXScreen::REVERSED_L_SHAPE
436                                 : LyXScreen::L_SHAPE;
437                 y += cursor.y() + theLockingInset()->insetInInsetY();
438                 pimpl_->screen_->showManualCursor(text, x, y, asc, desc,
439                                                   shape);
440         }
441 }
442
443
444 void BufferView::hideLockedInsetCursor()
445 {
446         if (theLockingInset() && available()) {
447                 pimpl_->screen_->hideCursor();
448         }
449 }
450
451
452 void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
453 {
454         if (theLockingInset() && available()) {
455                 y += text->cursor.y() + theLockingInset()->insetInInsetY();
456                 if (pimpl_->screen_->fitManualCursor(text, this, x, y, asc, desc))
457                         updateScrollbar();
458         }
459 }
460
461
462 int BufferView::unlockInset(UpdatableInset * inset)
463 {
464         if (inset && theLockingInset() == inset) {
465                 inset->insetUnlock(this);
466                 theLockingInset(0);
467                 // make sure we update the combo !
468                 owner()->setLayout(getLyXText()->cursor.par()->getLayout());
469                 finishUndo();
470                 return 0;
471         } else if (inset && theLockingInset() &&
472                    theLockingInset()->unlockInsetInInset(this, inset)) {
473                 // owner inset has updated the layout combo 
474                 finishUndo();
475                 return 0;
476         }
477         return bufferlist.unlockInset(inset);
478 }
479
480
481 void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind)
482 {
483         if (!theLockingInset())
484                 return; // shouldn't happen
485         if (kind == Undo::EDIT) // in this case insets would not be stored!
486                 kind = Undo::FINISH;
487         setUndo(this, kind,
488                 text->cursor.par(),
489                 text->cursor.par()->next());
490 }
491
492
493 void BufferView::updateInset(Inset * inset, bool mark_dirty)
494 {
495         pimpl_->updateInset(inset, mark_dirty);
496 }
497
498
499 bool BufferView::ChangeInsets(Inset::Code code,
500                               string const & from, string const & to)
501 {
502         bool need_update = false;
503         LyXCursor cursor = text->cursor;
504         LyXCursor tmpcursor = cursor;
505         cursor.par(tmpcursor.par());
506         cursor.pos(tmpcursor.pos());
507
508         ParIterator end = buffer()->par_iterator_end();
509         for (ParIterator it = buffer()->par_iterator_begin();
510              it != end; ++it) {
511                 Paragraph * par = *it;
512                 bool changed_inset = false;
513                 for (Paragraph::inset_iterator it2 = par->inset_iterator_begin();
514                      it2 != par->inset_iterator_end(); ++it2) {
515                         if ((*it2)->lyxCode() == code) {
516                                 InsetCommand * inset = static_cast<InsetCommand *>(*it2);
517                                 if (inset->getContents() == from) {
518                                         inset->setContents(to);
519                                         changed_inset = true;
520                                 }
521                         }
522                 }
523                 if (changed_inset) {
524                         need_update = true;
525 #ifdef WITH_WARNINGS
526 #warning FIXME
527 #endif
528                         // The test it.size()==1 was needed to prevent crashes.
529                         // How to set the cursor corretly when it.size()>1 ??
530                         if (it.size() == 1) {
531                                 text->setCursorIntern(this, par, 0);
532                                 text->redoParagraphs(this, text->cursor,
533                                                      text->cursor.par()->next());
534                                 text->fullRebreak(this);
535                         }
536                 }
537         }
538         text->setCursorIntern(this, cursor.par(), cursor.pos());
539         return need_update;
540 }
541
542
543 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
544 {
545         // Check if the label 'from' appears more than once
546         vector<string> labels = buffer()->getLabelList();
547         if (count(labels.begin(), labels.end(), from) > 1)
548                 return false;
549
550         return ChangeInsets(Inset::REF_CODE, from, to);
551 }
552
553
554 bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to)
555 {
556
557         vector<pair<string,string> > keys = buffer()->getBibkeyList();  
558         if (count_if(keys.begin(), keys.end(), 
559                      lyx::equal_1st_in_pair<string,string>(from)) 
560             > 1)
561                 return false;
562
563         return ChangeInsets(Inset::CITE_CODE, from, to);
564 }
565
566
567 UpdatableInset * BufferView::theLockingInset() const
568 {
569         // If NULL is not allowed we should put an Assert here. (Lgb)
570         if (text)
571                 return text->the_locking_inset;
572         return 0;
573 }
574
575
576 void BufferView::theLockingInset(UpdatableInset * inset)
577 {
578         text->the_locking_inset = inset;
579 }
580
581
582 LyXText * BufferView::getLyXText() const
583 {
584         if (theLockingInset()) {
585                 LyXText * txt = theLockingInset()->getLyXText(this, true);
586                 if (txt)
587                         return txt;
588         }
589         return text;
590 }
591
592
593 LyXText * BufferView::getParentText(Inset * inset) const
594 {
595         if (inset->owner()) {
596                 LyXText * txt = inset->getLyXText(this);
597                 inset = inset->owner();
598                 while (inset && inset->getLyXText(this) == txt)
599                         inset = inset->owner();
600                 if (inset)
601                         return inset->getLyXText(this);
602         }
603         return text;
604 }
605
606
607 Language const * BufferView::getParentLanguage(Inset * inset) const
608 {
609         LyXText * text = getParentText(inset);
610         return text->cursor.par()->getFontSettings(buffer()->params,
611                                                    text->cursor.pos()).language();
612 }