]> git.lyx.org Git - lyx.git/blob - src/BufferView.C
Whitespace.
[lyx.git] / src / BufferView.C
1 /**
2  * \file BufferView.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braustein
7  * \author Lars Gullik Bjønnes
8  * \author John Levon
9  * \author André Pönitz
10  * \author Jürgen Vigna
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "BufferView.h"
18 #include "BufferView_pimpl.h"
19
20 #include "LaTeX.h"
21 #include "ParagraphParameters.h"
22 #include "WordLangTuple.h"
23 #include "buffer.h"
24 #include "bufferlist.h"
25 #include "debug.h"
26 #include "gettext.h"
27 #include "errorlist.h"
28 #include "iterators.h"
29 #include "language.h"
30 #include "lyxcursor.h"
31 #include "lyxlex.h"
32 #include "lyxtext.h"
33 #include "undo_funcs.h"
34 #include "changes.h"
35 #include "paragraph_funcs.h"
36
37 #include "frontends/Alert.h"
38 #include "frontends/Dialogs.h"
39 #include "frontends/LyXView.h"
40 #include "frontends/WorkArea.h"
41 #include "frontends/screen.h"
42
43 #include "insets/insetcommand.h" // ChangeRefs
44 #include "insets/updatableinset.h"
45
46 #include "support/FileInfo.h"
47 #include "support/filetools.h"
48 #include "support/types.h"
49 #include "support/lyxalgo.h" // lyx_count
50
51 #include <fstream>
52
53 extern BufferList bufferlist;
54
55 using lyx::pos_type;
56 using namespace lyx::support;
57
58 using std::pair;
59 using std::endl;
60 using std::ifstream;
61 using std::vector;
62 using std::find;
63 using std::count_if;
64
65
66 BufferView::BufferView(LyXView * owner, int xpos, int ypos,
67                        int width, int height)
68         : pimpl_(new Pimpl(this, owner, xpos, ypos, width, height))
69 {
70         text = 0;
71 }
72
73
74 BufferView::~BufferView()
75 {
76         delete text;
77         delete pimpl_;
78 }
79
80
81 Buffer * BufferView::buffer() const
82 {
83         return pimpl_->buffer_;
84 }
85
86
87 LyXScreen & BufferView::screen() const
88 {
89         return pimpl_->screen();
90 }
91
92
93 LyXView * BufferView::owner() const
94 {
95         return pimpl_->owner_;
96 }
97
98
99 Painter & BufferView::painter() const
100 {
101         return pimpl_->painter();
102 }
103
104
105 void BufferView::buffer(Buffer * b)
106 {
107         pimpl_->buffer(b);
108 }
109
110
111 bool BufferView::newFile(string const & fn, string const & tn, bool named)
112 {
113         return pimpl_->newFile(fn, tn, named);
114 }
115
116
117 bool BufferView::loadLyXFile(string const & fn, bool tl)
118 {
119         return pimpl_->loadLyXFile(fn, tl);
120 }
121
122
123 void BufferView::reload()
124 {
125         string const fn = buffer()->fileName();
126         if (bufferlist.close(buffer(), false))
127                 loadLyXFile(fn);
128 }
129
130
131 void BufferView::resize()
132 {
133         if (pimpl_->buffer_)
134                 pimpl_->resizeCurrentBuffer();
135 }
136
137
138 bool BufferView::fitCursor()
139 {
140         return pimpl_->fitCursor();
141 }
142
143
144 void BufferView::update()
145 {
146         pimpl_->update();
147 }
148
149
150 void BufferView::updateScrollbar()
151 {
152         pimpl_->updateScrollbar();
153 }
154
155
156 void BufferView::scrollDocView(int value)
157 {
158         pimpl_->scrollDocView(value);
159 }
160
161
162 void BufferView::redoCurrentBuffer()
163 {
164         pimpl_->redoCurrentBuffer();
165 }
166
167
168 bool BufferView::available() const
169 {
170         return pimpl_->available();
171 }
172
173
174 Change const BufferView::getCurrentChange()
175 {
176         return pimpl_->getCurrentChange();
177 }
178
179
180 void BufferView::beforeChange(LyXText * text)
181 {
182         pimpl_->beforeChange(text);
183 }
184
185
186 void BufferView::savePosition(unsigned int i)
187 {
188         pimpl_->savePosition(i);
189 }
190
191
192 void BufferView::restorePosition(unsigned int i)
193 {
194         pimpl_->restorePosition(i);
195 }
196
197
198 bool BufferView::isSavedPosition(unsigned int i)
199 {
200         return pimpl_->isSavedPosition(i);
201 }
202
203
204 void BufferView::switchKeyMap()
205 {
206         pimpl_->switchKeyMap();
207 }
208
209
210 void BufferView::insetUnlock()
211 {
212         pimpl_->insetUnlock();
213 }
214
215
216 int BufferView::workWidth() const
217 {
218         return pimpl_->workarea().workWidth();
219 }
220
221
222 void BufferView::center()
223 {
224         pimpl_->center();
225 }
226
227
228 string const BufferView::getClipboard() const
229 {
230         return pimpl_->workarea().getClipboard();
231 }
232
233
234 void BufferView::stuffClipboard(string const & stuff) const
235 {
236         pimpl_->stuffClipboard(stuff);
237 }
238
239
240 bool BufferView::dispatch(FuncRequest const & ev)
241 {
242         return pimpl_->dispatch(ev);
243 }
244
245
246 void BufferView::scroll(int lines)
247 {
248         pimpl_->scroll(lines);
249 }
250
251
252 // Inserts a file into current document
253 bool BufferView::insertLyXFile(string const & filen)
254         //
255         // Copyright CHT Software Service GmbH
256         // Uwe C. Schroeder
257         //
258         // Insert a LyXformat - file into current buffer
259         //
260         // Moved from lyx_cb.C (Lgb)
261 {
262         if (filen.empty())
263                 return false;
264
265         string const fname = MakeAbsPath(filen);
266
267         // check if file exist
268         FileInfo const fi(fname);
269
270         if (!fi.readable()) {
271                 string const file = MakeDisplayPath(fname, 50);
272                 string const text =
273                         bformat(_("The specified document\n%1$s\ncould not be read."), file);
274                 Alert::error(_("Could not read document"), text);
275                 return false;
276         }
277
278         beforeChange(text);
279
280         ifstream ifs(fname.c_str());
281         if (!ifs) {
282                 string const file = MakeDisplayPath(fname, 50);
283                 string const text =
284                         bformat(_("Could not open the specified document %1$s\n"), file);
285                 Alert::error(_("Could not open file"), text);
286                 return false;
287         }
288
289         int const c = ifs.peek();
290
291         LyXLex lex(0, 0);
292         lex.setStream(ifs);
293
294         bool res = true;
295
296         text->breakParagraph(buffer()->paragraphs);
297
298         if (c == '#') {
299                 // FIXME: huh ? No we won't !
300                 lyxerr[Debug::INFO] << "Will insert file with header" << endl;
301                 res = buffer()->readFile(lex, fname, ParagraphList::iterator(text->cursor.par()));
302         } else {
303                 lyxerr[Debug::INFO] << "Will insert file without header"
304                                     << endl;
305                 res = buffer()->readBody(lex, ParagraphList::iterator(text->cursor.par()));
306         }
307
308         resize();
309         return res;
310 }
311
312
313 void BufferView::showErrorList(string const & action) const
314 {
315         if (getErrorList().size()) {
316                 string const title = bformat(_("LyX: %1$s errors (%2$s)"), action, buffer()->fileName());
317                 owner()->getDialogs().show("errorlist", title);
318                 pimpl_->errorlist_.clear();
319         }
320 }
321
322
323 ErrorList const &
324 BufferView::getErrorList() const
325 {
326         return pimpl_->errorlist_;
327 }
328
329
330 void BufferView::setCursorFromRow(int row)
331 {
332         int tmpid = -1;
333         int tmppos = -1;
334
335         buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
336
337         ParagraphList::iterator texrowpar;
338
339         if (tmpid == -1) {
340                 texrowpar = text->ownerParagraphs().begin();
341                 tmppos = 0;
342         } else {
343                 texrowpar = buffer()->getParFromID(tmpid).pit();
344         }
345         text->setCursor(texrowpar, tmppos);
346 }
347
348
349 bool BufferView::insertInset(InsetOld * inset, string const & lout)
350 {
351         return pimpl_->insertInset(inset, lout);
352 }
353
354
355 void BufferView::gotoLabel(string const & label)
356 {
357         for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
358              it != buffer()->inset_iterator_end(); ++it) {
359                 vector<string> labels;
360                 it->getLabelList(labels);
361                 if (find(labels.begin(),labels.end(),label) != labels.end()) {
362                         beforeChange(text);
363                         text->setCursor(it.getPar(), it.getPos());
364                         text->selection.cursor = text->cursor;
365                         update();
366                         return;
367                 }
368         }
369 }
370
371
372 void BufferView::undo()
373 {
374         if (!available())
375                 return;
376
377         owner()->message(_("Undo"));
378         beforeChange(text);
379         if (!textUndo(this))
380                 owner()->message(_("No further undo information"));
381         update();
382         switchKeyMap();
383 }
384
385
386 void BufferView::redo()
387 {
388         if (!available())
389                 return;
390
391         owner()->message(_("Redo"));
392         beforeChange(text);
393         if (!textRedo(this))
394                 owner()->message(_("No further redo information"));
395         update();
396         switchKeyMap();
397 }
398
399
400 // these functions are for the spellchecker
401 WordLangTuple const BufferView::nextWord(float & value)
402 {
403         if (!available()) {
404                 value = 1;
405                 return WordLangTuple();
406         }
407
408         return text->selectNextWordToSpellcheck(value);
409 }
410
411
412 void BufferView::selectLastWord()
413 {
414         if (!available())
415                 return;
416
417         LyXCursor cur = text->selection.cursor;
418         beforeChange(text);
419         text->selection.cursor = cur;
420         text->selectSelectedWord();
421         update();
422 }
423
424
425 void BufferView::endOfSpellCheck()
426 {
427         if (!available()) return;
428
429         beforeChange(text);
430         text->selectSelectedWord();
431         text->clearSelection();
432         update();
433 }
434
435
436 void BufferView::replaceWord(string const & replacestring)
437 {
438         if (!available())
439                 return;
440
441         LyXText * tt = getLyXText();
442
443         tt->replaceSelectionWithString(replacestring);
444         tt->setSelectionRange(replacestring.length());
445
446         // Go back so that replacement string is also spellchecked
447         for (string::size_type i = 0; i < replacestring.length() + 1; ++i) 
448                 tt->cursorLeft(this);
449
450         // FIXME: should be done through LFUN
451         buffer()->markDirty();
452         update();
453 }
454
455
456 bool BufferView::lockInset(UpdatableInset * inset)
457 {
458         if (!inset)
459                 return false;
460         // don't relock if we're already locked
461         if (theLockingInset() == inset)
462                 return true;
463         if (!theLockingInset()) {
464                 // first check if it's the inset under the cursor we want lock
465                 // should be most of the time
466                 if (text->cursor.pos() < text->cursor.par()->size()
467                     && text->cursor.par()->getChar(text->cursor.pos()) ==
468                     Paragraph::META_INSET) {
469                         InsetOld * in = text->cursor.par()->getInset(text->cursor.pos());
470                         if (inset == in) {
471                                 theLockingInset(inset);
472                                 return true;
473                         }
474                 }
475                 // Then do a deep look of the inset and lock the right one
476                 int const id = inset->id();
477                 ParagraphList::iterator pit = buffer()->paragraphs.begin();
478                 ParagraphList::iterator pend = buffer()->paragraphs.end();
479                 for (; pit != pend; ++pit) {
480                         InsetList::iterator it = pit->insetlist.begin();
481                         InsetList::iterator end = pit->insetlist.end();
482                         for (; it != end; ++it) {
483                                 if (it->inset == inset) {
484                                         text->setCursorIntern(pit, it->pos);
485                                         theLockingInset(inset);
486                                         return true;
487                                 }
488                                 if (it->inset->getInsetFromID(id)) {
489                                         text->setCursorIntern(pit, it->pos);
490                                         FuncRequest cmd(this, LFUN_INSET_EDIT, "left");
491                                         it->inset->localDispatch(cmd);
492                                         return theLockingInset()->lockInsetInInset(this, inset);
493                                 }
494                         }
495                 }
496                 return false;
497         }
498         return theLockingInset()->lockInsetInInset(this, inset);
499 }
500
501
502 bool BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
503 {
504         if (theLockingInset() && available()) {
505                 y += text->cursor.iy() + theLockingInset()->insetInInsetY();
506                 if (screen().fitManualCursor(this, text, x, y, asc, desc)) {
507                         updateScrollbar();
508                         return true;
509                 }
510         }
511         return false;
512 }
513
514
515 void BufferView::hideCursor()
516 {
517         screen().hideCursor();
518 }
519
520
521 int BufferView::unlockInset(UpdatableInset * inset)
522 {
523         if (!inset)
524                 return 0;
525         if (inset && theLockingInset() == inset) {
526                 inset->insetUnlock(this);
527                 theLockingInset(0);
528                 // make sure we update the combo !
529                 owner()->setLayout(getLyXText()->cursor.par()->layout()->name());
530                 // Tell the paragraph dialog that we changed paragraph
531                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
532                 finishUndo();
533                 return 0;
534         } else if (inset && theLockingInset() &&
535                    theLockingInset()->unlockInsetInInset(this, inset)) {
536                 // Tell the paragraph dialog that we changed paragraph
537                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
538                 // owner inset has updated the layout combo
539                 finishUndo();
540                 return 0;
541         }
542         return 1;
543 }
544
545
546 void BufferView::updateInset()
547 {
548         pimpl_->updateInset();
549 }
550
551
552 bool BufferView::ChangeInsets(InsetOld::Code code,
553                               string const & from, string const & to)
554 {
555         bool need_update = false;
556         LyXCursor cursor = text->cursor;
557         LyXCursor tmpcursor = cursor;
558         cursor.par(tmpcursor.par());
559         cursor.pos(tmpcursor.pos());
560
561         ParIterator end = buffer()->par_iterator_end();
562         for (ParIterator it = buffer()->par_iterator_begin();
563              it != end; ++it) {
564                 bool changed_inset = false;
565                 for (InsetList::iterator it2 = it->insetlist.begin();
566                      it2 != it->insetlist.end(); ++it2) {
567                         if (it2->inset->lyxCode() == code) {
568                                 InsetCommand * inset = static_cast<InsetCommand *>(it2->inset);
569                                 if (inset->getContents() == from) {
570                                         inset->setContents(to);
571                                         changed_inset = true;
572                                 }
573                         }
574                 }
575                 if (changed_inset) {
576                         need_update = true;
577
578                         // FIXME
579
580                         // The test it.size()==1 was needed to prevent crashes.
581                         // How to set the cursor corretly when it.size()>1 ??
582                         if (it.size() == 1) {
583                                 text->setCursorIntern(it.pit(), 0);
584                                 text->redoParagraph(text->cursor.par());
585                         }
586                 }
587         }
588         text->setCursorIntern(cursor.par(), cursor.pos());
589         return need_update;
590 }
591
592
593 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
594 {
595         // Check if the label 'from' appears more than once
596         vector<string> labels;
597         buffer()->getLabelList(labels);
598
599         if (lyx::count(labels.begin(), labels.end(), from) > 1)
600                 return false;
601
602         return ChangeInsets(InsetOld::REF_CODE, from, to);
603 }
604
605
606 UpdatableInset * BufferView::theLockingInset() const
607 {
608         // If NULL is not allowed we should put an Assert here. (Lgb)
609         if (text)
610                 return text->the_locking_inset;
611         return 0;
612 }
613
614
615 void BufferView::theLockingInset(UpdatableInset * inset)
616 {
617         text->the_locking_inset = inset;
618 }
619
620
621 LyXText * BufferView::getLyXText() const
622 {
623         if (theLockingInset()) {
624                 LyXText * txt = theLockingInset()->getLyXText(this, true);
625                 if (txt)
626                         return txt;
627         }
628         return text;
629 }
630
631
632 LyXText * BufferView::getParentText(InsetOld * inset) const
633 {
634         if (inset->owner()) {
635                 LyXText * txt = inset->getLyXText(this);
636                 inset = inset->owner();
637                 while (inset && inset->getLyXText(this) == txt)
638                         inset = inset->owner();
639                 if (inset)
640                         return inset->getLyXText(this);
641         }
642         return text;
643 }
644
645
646 Language const * BufferView::getParentLanguage(InsetOld * inset) const
647 {
648         LyXText * text = getParentText(inset);
649         return text->cursor.par()->getFontSettings(buffer()->params,
650                                                    text->cursor.pos()).language();
651 }
652
653
654 Encoding const * BufferView::getEncoding() const
655 {
656         LyXText * t = getLyXText();
657         if (!t)
658                 return 0;
659
660         LyXCursor const & c = t->cursor;
661         LyXFont const font = c.par()->getFont(buffer()->params, c.pos(),
662                                               outerFont(c.par(), t->ownerParagraphs()));
663         return font.language()->encoding();
664 }
665
666
667 void BufferView::haveSelection(bool sel)
668 {
669         pimpl_->workarea().haveSelection(sel);
670 }
671
672
673 int BufferView::workHeight() const
674 {
675         return pimpl_->workarea().workHeight();
676 }