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