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