]> git.lyx.org Git - lyx.git/blob - src/BufferView.C
Add a couple of new functions.
[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 int BufferView::top_y() const
229 {
230         return pimpl_->top_y();
231 }
232
233
234 void BufferView::top_y(int y)
235 {
236         pimpl_->top_y(y);
237 }
238
239
240 string const BufferView::getClipboard() const
241 {
242         return pimpl_->workarea().getClipboard();
243 }
244
245
246 void BufferView::stuffClipboard(string const & stuff) const
247 {
248         pimpl_->stuffClipboard(stuff);
249 }
250
251
252 bool BufferView::dispatch(FuncRequest const & ev)
253 {
254         return pimpl_->dispatch(ev);
255 }
256
257
258 void BufferView::scroll(int lines)
259 {
260         pimpl_->scroll(lines);
261 }
262
263
264 // Inserts a file into current document
265 bool BufferView::insertLyXFile(string const & filen)
266         //
267         // Copyright CHT Software Service GmbH
268         // Uwe C. Schroeder
269         //
270         // Insert a LyXformat - file into current buffer
271         //
272         // Moved from lyx_cb.C (Lgb)
273 {
274         if (filen.empty())
275                 return false;
276
277         string const fname = MakeAbsPath(filen);
278
279         // check if file exist
280         FileInfo const fi(fname);
281
282         if (!fi.readable()) {
283                 string const file = MakeDisplayPath(fname, 50);
284                 string const text =
285                         bformat(_("The specified document\n%1$s\ncould not be read."), file);
286                 Alert::error(_("Could not read document"), text);
287                 return false;
288         }
289
290         beforeChange(text);
291
292         ifstream ifs(fname.c_str());
293         if (!ifs) {
294                 string const file = MakeDisplayPath(fname, 50);
295                 string const text =
296                         bformat(_("Could not open the specified document %1$s\n"), file);
297                 Alert::error(_("Could not open file"), text);
298                 return false;
299         }
300
301         int const c = ifs.peek();
302
303         LyXLex lex(0, 0);
304         lex.setStream(ifs);
305
306         bool res = true;
307
308         text->breakParagraph(buffer()->paragraphs);
309
310         if (c == '#') {
311                 // FIXME: huh ? No we won't !
312                 lyxerr[Debug::INFO] << "Will insert file with header" << endl;
313                 res = buffer()->readFile(lex, fname, ParagraphList::iterator(text->cursor.par()));
314         } else {
315                 lyxerr[Debug::INFO] << "Will insert file without header"
316                                     << endl;
317                 res = buffer()->readBody(lex, ParagraphList::iterator(text->cursor.par()));
318         }
319
320         resize();
321         return res;
322 }
323
324
325 void BufferView::showErrorList(string const & action) const
326 {
327         if (getErrorList().size()) {
328                 string const title = bformat(_("LyX: %1$s errors (%2$s)"), action, buffer()->fileName());
329                 owner()->getDialogs().show("errorlist", title);
330                 pimpl_->errorlist_.clear();
331         }
332 }
333
334
335 ErrorList const &
336 BufferView::getErrorList() const
337 {
338         return pimpl_->errorlist_;
339 }
340
341
342 void BufferView::setCursorFromRow(int row)
343 {
344         int tmpid = -1;
345         int tmppos = -1;
346
347         buffer()->texrow.getIdFromRow(row, tmpid, tmppos);
348
349         ParagraphList::iterator texrowpar;
350
351         if (tmpid == -1) {
352                 texrowpar = text->ownerParagraphs().begin();
353                 tmppos = 0;
354         } else {
355                 texrowpar = buffer()->getParFromID(tmpid).pit();
356         }
357         text->setCursor(texrowpar, tmppos);
358 }
359
360
361 bool BufferView::insertInset(InsetOld * inset, string const & lout)
362 {
363         return pimpl_->insertInset(inset, lout);
364 }
365
366
367 void BufferView::gotoLabel(string const & label)
368 {
369         for (Buffer::inset_iterator it = buffer()->inset_iterator_begin();
370              it != buffer()->inset_iterator_end(); ++it) {
371                 vector<string> labels;
372                 it->getLabelList(labels);
373                 if (find(labels.begin(),labels.end(),label) != labels.end()) {
374                         beforeChange(text);
375                         text->setCursor(it.getPar(), it.getPos());
376                         text->selection.cursor = text->cursor;
377                         update();
378                         return;
379                 }
380         }
381 }
382
383
384 void BufferView::undo()
385 {
386         if (!available())
387                 return;
388
389         owner()->message(_("Undo"));
390         beforeChange(text);
391         if (!textUndo(this))
392                 owner()->message(_("No further undo information"));
393         update();
394         switchKeyMap();
395 }
396
397
398 void BufferView::redo()
399 {
400         if (!available())
401                 return;
402
403         owner()->message(_("Redo"));
404         beforeChange(text);
405         if (!textRedo(this))
406                 owner()->message(_("No further redo information"));
407         update();
408         switchKeyMap();
409 }
410
411
412 // these functions are for the spellchecker
413 WordLangTuple const BufferView::nextWord(float & value)
414 {
415         if (!available()) {
416                 value = 1;
417                 return WordLangTuple();
418         }
419
420         return text->selectNextWordToSpellcheck(value);
421 }
422
423
424 void BufferView::selectLastWord()
425 {
426         if (!available())
427                 return;
428
429         LyXCursor cur = text->selection.cursor;
430         beforeChange(text);
431         text->selection.cursor = cur;
432         text->selectSelectedWord();
433         update();
434 }
435
436
437 void BufferView::endOfSpellCheck()
438 {
439         if (!available()) return;
440
441         beforeChange(text);
442         text->selectSelectedWord();
443         text->clearSelection();
444         update();
445 }
446
447
448 void BufferView::replaceWord(string const & replacestring)
449 {
450         if (!available())
451                 return;
452
453         LyXText * tt = getLyXText();
454
455         tt->replaceSelectionWithString(replacestring);
456         tt->setSelectionRange(replacestring.length());
457
458         // Go back so that replacement string is also spellchecked
459         for (string::size_type i = 0; i < replacestring.length() + 1; ++i)
460                 tt->cursorLeft(this);
461
462         // FIXME: should be done through LFUN
463         buffer()->markDirty();
464         update();
465 }
466
467
468 bool BufferView::lockInset(UpdatableInset * inset)
469 {
470         if (!inset)
471                 return false;
472         // don't relock if we're already locked
473         if (theLockingInset() == inset)
474                 return true;
475         if (!theLockingInset()) {
476                 // first check if it's the inset under the cursor we want lock
477                 // should be most of the time
478                 if (text->cursor.pos() < text->cursor.par()->size()
479                     && text->cursor.par()->getChar(text->cursor.pos()) ==
480                     Paragraph::META_INSET) {
481                         InsetOld * in = text->cursor.par()->getInset(text->cursor.pos());
482                         if (inset == in) {
483                                 theLockingInset(inset);
484                                 return true;
485                         }
486                 }
487                 // Then do a deep look of the inset and lock the right one
488                 int const id = inset->id();
489                 ParagraphList::iterator pit = buffer()->paragraphs.begin();
490                 ParagraphList::iterator pend = buffer()->paragraphs.end();
491                 for (; pit != pend; ++pit) {
492                         InsetList::iterator it = pit->insetlist.begin();
493                         InsetList::iterator end = pit->insetlist.end();
494                         for (; it != end; ++it) {
495                                 if (it->inset == inset) {
496                                         text->setCursorIntern(pit, it->pos);
497                                         theLockingInset(inset);
498                                         return true;
499                                 }
500                                 if (it->inset->getInsetFromID(id)) {
501                                         text->setCursorIntern(pit, it->pos);
502                                         FuncRequest cmd(this, LFUN_INSET_EDIT, "left");
503                                         it->inset->localDispatch(cmd);
504                                         return theLockingInset()->lockInsetInInset(this, inset);
505                                 }
506                         }
507                 }
508                 return false;
509         }
510         return theLockingInset()->lockInsetInInset(this, inset);
511 }
512
513
514 bool BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc)
515 {
516         if (theLockingInset() && available()) {
517                 y += text->cursor.iy() + theLockingInset()->insetInInsetY();
518                 if (screen().fitManualCursor(this, text, x, y, asc, desc)) {
519                         updateScrollbar();
520                         return true;
521                 }
522         }
523         return false;
524 }
525
526
527 void BufferView::hideCursor()
528 {
529         screen().hideCursor();
530 }
531
532
533 int BufferView::unlockInset(UpdatableInset * inset)
534 {
535         if (!inset)
536                 return 0;
537         if (inset && theLockingInset() == inset) {
538                 inset->insetUnlock(this);
539                 theLockingInset(0);
540                 // make sure we update the combo !
541                 owner()->setLayout(getLyXText()->cursor.par()->layout()->name());
542                 // Tell the paragraph dialog that we changed paragraph
543                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
544                 finishUndo();
545                 return 0;
546         } else if (inset && theLockingInset() &&
547                    theLockingInset()->unlockInsetInInset(this, inset)) {
548                 // Tell the paragraph dialog that we changed paragraph
549                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
550                 // owner inset has updated the layout combo
551                 finishUndo();
552                 return 0;
553         }
554         return 1;
555 }
556
557
558 void BufferView::updateInset(InsetOld const * inset)
559 {
560         pimpl_->updateInset(inset);
561 }
562
563
564 bool BufferView::ChangeInsets(InsetOld::Code code,
565                               string const & from, string const & to)
566 {
567         bool need_update = false;
568         LyXCursor cursor = text->cursor;
569         LyXCursor tmpcursor = cursor;
570         cursor.par(tmpcursor.par());
571         cursor.pos(tmpcursor.pos());
572
573         ParIterator end = buffer()->par_iterator_end();
574         for (ParIterator it = buffer()->par_iterator_begin();
575              it != end; ++it) {
576                 bool changed_inset = false;
577                 for (InsetList::iterator it2 = it->insetlist.begin();
578                      it2 != it->insetlist.end(); ++it2) {
579                         if (it2->inset->lyxCode() == code) {
580                                 InsetCommand * inset = static_cast<InsetCommand *>(it2->inset);
581                                 if (inset->getContents() == from) {
582                                         inset->setContents(to);
583                                         changed_inset = true;
584                                 }
585                         }
586                 }
587                 if (changed_inset) {
588                         need_update = true;
589
590                         // FIXME
591
592                         // The test it.size()==1 was needed to prevent crashes.
593                         // How to set the cursor corretly when it.size()>1 ??
594                         if (it.size() == 1) {
595                                 text->setCursorIntern(it.pit(), 0);
596                                 text->redoParagraph(text->cursor.par());
597                         }
598                 }
599         }
600         text->setCursorIntern(cursor.par(), cursor.pos());
601         return need_update;
602 }
603
604
605 bool BufferView::ChangeRefsIfUnique(string const & from, string const & to)
606 {
607         // Check if the label 'from' appears more than once
608         vector<string> labels;
609         buffer()->getLabelList(labels);
610
611         if (lyx::count(labels.begin(), labels.end(), from) > 1)
612                 return false;
613
614         return ChangeInsets(InsetOld::REF_CODE, from, to);
615 }
616
617
618 UpdatableInset * BufferView::theLockingInset() const
619 {
620         // If NULL is not allowed we should put an Assert here. (Lgb)
621         if (text)
622                 return text->the_locking_inset;
623         return 0;
624 }
625
626
627 void BufferView::theLockingInset(UpdatableInset * inset)
628 {
629         text->the_locking_inset = inset;
630 }
631
632
633 LyXText * BufferView::getLyXText() const
634 {
635         if (theLockingInset()) {
636                 LyXText * txt = theLockingInset()->getLyXText(this, true);
637                 if (txt)
638                         return txt;
639         }
640         return text;
641 }
642
643
644 Language const * BufferView::getParentLanguage(InsetOld * inset) const
645 {
646         Paragraph const & par = ownerPar(*buffer(), inset);
647         return par.getFontSettings(buffer()->params,
648                                    par.getPositionOfInset(inset)).language();
649 }
650
651
652 Encoding const * BufferView::getEncoding() const
653 {
654         LyXText * t = getLyXText();
655         if (!t)
656                 return 0;
657
658         LyXCursor const & c = t->cursor;
659         LyXFont const font = c.par()->getFont(buffer()->params, c.pos(),
660                                               outerFont(c.par(), t->ownerParagraphs()));
661         return font.language()->encoding();
662 }
663
664
665 void BufferView::haveSelection(bool sel)
666 {
667         pimpl_->workarea().haveSelection(sel);
668 }
669
670
671 int BufferView::workHeight() const
672 {
673         return pimpl_->workarea().workHeight();
674 }