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