]> git.lyx.org Git - lyx.git/blob - src/DocIterator.cpp
Fix text direction issue for InsetInfo in RTL context
[lyx.git] / src / DocIterator.cpp
1 /**
2  * \file DocIterator.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Alfredo Braunstein
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12
13 #include <config.h>
14
15 #include "DocIterator.h"
16
17 #include "Buffer.h"
18 #include "BufferParams.h"
19 #include "Encoding.h"
20 #include "Font.h"
21 #include "InsetList.h"
22 #include "Language.h"
23 #include "Paragraph.h"
24 #include "Text.h"
25
26 #include "mathed/MathData.h"
27 #include "mathed/InsetMath.h"
28 #include "mathed/InsetMathHull.h"
29
30 #include "insets/InsetTabular.h"
31
32 #include "support/convert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/gettext.h"
36 #include "support/lassert.h"
37 #include "support/lstrings.h"
38
39 #include <ostream>
40
41 using namespace std;
42 using namespace lyx::support;
43
44 namespace lyx {
45
46
47 DocIterator::DocIterator()
48         : boundary_(false), inset_(0), buffer_(0)
49 {}
50
51
52 // We could be able to get rid of this if only every BufferView were
53 // associated to a buffer on construction.
54 DocIterator::DocIterator(Buffer * buf)
55         : boundary_(false), inset_(0), buffer_(buf)
56 {}
57
58
59 DocIterator::DocIterator(Buffer * buf, Inset * inset)
60         : boundary_(false), inset_(inset), buffer_(buf)
61 {}
62
63
64 DocIterator doc_iterator_begin(const Buffer * buf0, const Inset * inset0)
65 {
66         Buffer * buf = const_cast<Buffer *>(buf0);
67         Inset * inset = const_cast<Inset *>(inset0);
68         DocIterator dit(buf, inset ? inset : &buf->inset());
69         dit.forwardPos();
70         return dit;
71 }
72
73
74 DocIterator doc_iterator_end(const Buffer * buf0, const Inset * inset0)
75 {
76         Buffer * buf = const_cast<Buffer *>(buf0);
77         Inset * inset = const_cast<Inset *>(inset0);
78         return DocIterator(buf, inset ? inset : &buf->inset());
79 }
80
81
82 DocIterator DocIterator::clone(Buffer * buffer) const
83 {
84         LASSERT(buffer->isClone(), return DocIterator());
85         Inset * inset = &buffer->inset();
86         DocIterator dit(buffer);
87         size_t const n = slices_.size();
88         for (size_t i = 0 ; i != n; ++i) {
89                 LBUFERR(inset);
90                 dit.push_back(slices_[i]);
91                 dit.top().inset_ = inset;
92                 if (i + 1 != n)
93                         inset = dit.nextInset();
94         }
95         return dit;
96 }
97
98
99 bool DocIterator::inRegexped() const
100 {
101         InsetMath * im = inset().asInsetMath();
102         if (!im)
103                 return false;
104         InsetMathHull * hull = im->asHullInset();
105         return hull && hull->getType() == hullRegexp;
106 }
107
108
109 LyXErr & operator<<(LyXErr & os, DocIterator const & it)
110 {
111         os.stream() << it;
112         return os;
113 }
114
115
116 Inset * DocIterator::nextInset() const
117 {
118         LASSERT(!empty(), return 0);
119         if (pos() == lastpos())
120                 return 0;
121         if (pos() > lastpos()) {
122                 LYXERR0("Should not happen, but it does: pos() = "
123                         << pos() << ", lastpos() = " << lastpos());
124                 return 0;
125         }
126         if (inMathed())
127                 return nextAtom().nucleus();
128         return paragraph().getInset(pos());
129 }
130
131
132 Inset * DocIterator::prevInset() const
133 {
134         LASSERT(!empty(), return 0);
135         if (pos() == 0)
136                 return 0;
137         if (inMathed()) {
138                 if (cell().empty())
139                         // FIXME: this should not happen but it does.
140                         // See bug 3189
141                         // http://www.lyx.org/trac/ticket/3189
142                         return 0;
143                 else
144                         return prevAtom().nucleus();
145         }
146         return paragraph().getInset(pos() - 1);
147 }
148
149
150 Inset * DocIterator::realInset() const
151 {
152         LASSERT(inTexted(), return 0);
153         // if we are in a tabular, we need the cell
154         if (inset().lyxCode() == TABULAR_CODE) {
155                 InsetTabular * tabular = inset().asInsetTabular();
156                 return tabular->cell(idx()).get();
157         }
158         return &inset();
159 }
160
161
162 InsetMath & DocIterator::nextMath()
163 {
164         return *nextAtom().nucleus();
165 }
166
167
168 InsetMath & DocIterator::prevMath()
169 {
170         return *prevAtom().nucleus();
171 }
172
173
174 MathAtom & DocIterator::prevAtom() const
175 {
176         LASSERT(!empty(), /**/);
177         LASSERT(pos() > 0, /**/);
178         return cell()[pos() - 1];
179 }
180
181
182 MathAtom & DocIterator::nextAtom() const
183 {
184         LASSERT(!empty(), /**/);
185         //lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
186         LASSERT(pos() < lastpos(), /**/);
187         return cell()[pos()];
188 }
189
190
191 Text * DocIterator::text() const
192 {
193         LASSERT(!empty(), return 0);
194         return top().text();
195 }
196
197
198 Paragraph & DocIterator::paragraph() const
199 {
200         if (!inTexted()) {
201                 LYXERR0(*this);
202                 LBUFERR(false);
203         }
204         return top().paragraph();
205 }
206
207
208 Paragraph & DocIterator::innerParagraph() const
209 {
210         LBUFERR(!empty());
211         return innerTextSlice().paragraph();
212 }
213
214
215 FontSpan DocIterator::locateWord(word_location const loc) const
216 {
217         FontSpan f = FontSpan();
218
219         if (!top().text()->empty()) {
220                 f.first = pos();
221                 top().paragraph().locateWord(f.first, f.last, loc);
222         }
223         return f;
224 }
225
226
227 CursorSlice const & DocIterator::innerTextSlice() const
228 {
229         LBUFERR(!empty());
230         // go up until first non-0 text is hit
231         // (innermost text is 0 in mathed)
232         for (int i = depth() - 1; i >= 0; --i)
233                 if (slices_[i].text())
234                         return slices_[i];
235
236         // This case is in principle not possible. We _must_
237         // be inside a Text.
238         LBUFERR(false);
239         // Squash warning
240         static const CursorSlice c;
241         return c;
242 }
243
244
245 docstring DocIterator::paragraphGotoArgument() const
246 {
247         CursorSlice const & s = innerTextSlice();
248         return convert<docstring>(s.paragraph().id()) + ' ' +
249                 convert<docstring>(s.pos());
250 }
251
252
253 DocIterator DocIterator::getInnerText() const
254 {
255         DocIterator texted = *this;
256         while (!texted.inTexted())
257                 texted.pop_back();
258         return texted;
259 }
260
261
262 pit_type DocIterator::lastpit() const
263 {
264         return inMathed() ? 0 : text()->paragraphs().size() - 1;
265 }
266
267
268 pos_type DocIterator::lastpos() const
269 {
270         return inMathed() ? cell().size() : paragraph().size();
271 }
272
273
274 DocIterator::idx_type DocIterator::lastidx() const
275 {
276         return top().lastidx();
277 }
278
279
280 size_t DocIterator::nargs() const
281 {
282         // assume 1x1 grid for main text
283         return top().nargs();
284 }
285
286
287 size_t DocIterator::ncols() const
288 {
289         // assume 1x1 grid for main text
290         return top().ncols();
291 }
292
293
294 size_t DocIterator::nrows() const
295 {
296         // assume 1x1 grid for main text
297         return top().nrows();
298 }
299
300
301 DocIterator::row_type DocIterator::row() const
302 {
303         return top().row();
304 }
305
306
307 DocIterator::col_type DocIterator::col() const
308 {
309         return top().col();
310 }
311
312
313 MathData & DocIterator::cell() const
314 {
315 //      LASSERT(inMathed(), /**/);
316         return top().cell();
317 }
318
319
320 Text * DocIterator::innerText() const
321 {
322         LASSERT(!empty(), return 0);
323         return innerTextSlice().text();
324 }
325
326
327 Inset * DocIterator::innerInsetOfType(int code) const
328 {
329         for (int i = depth() - 1; i >= 0; --i)
330                 if (slices_[i].inset_->lyxCode() == code)
331                         return slices_[i].inset_;
332         return 0;
333 }
334
335
336 bool DocIterator::posBackward()
337 {
338         if (pos() == 0)
339                 return false;
340         --pos();
341         return true;
342 }
343
344
345 bool DocIterator::posForward()
346 {
347         if (pos() == lastpos())
348                 return false;
349         ++pos();
350         return true;
351 }
352
353
354 // This duplicates code above, but is in the critical path.
355 // So please think twice before adding stuff
356 void DocIterator::forwardPos()
357 {
358         // this dog bites his tail
359         if (empty()) {
360                 push_back(CursorSlice(*inset_));
361                 return;
362         }
363
364         CursorSlice & tip = top();
365         //lyxerr << "XXX\n" << *this << endl;
366
367         // not at cell/paragraph end?
368         if (tip.pos() != tip.lastpos()) {
369                 // move into an inset to the right if possible
370                 Inset * n = 0;
371                 if (inMathed())
372                         n = (tip.cell().begin() + tip.pos())->nucleus();
373                 else
374                         n = paragraph().getInset(tip.pos());
375                 if (n && n->isActive()) {
376                         //lyxerr << "... descend" << endl;
377                         push_back(CursorSlice(*n));
378                         return;
379                 }
380         }
381
382         // jump to the next cell/paragraph if possible
383         if (!tip.at_end()) {
384                 tip.forwardPos();
385                 return;
386         }
387
388         // otherwise leave inset and jump over inset as a whole
389         pop_back();
390         // 'tip' is invalid now...
391         if (!empty())
392                 ++top().pos();
393 }
394
395
396 void DocIterator::forwardPosIgnoreCollapsed()
397 {
398         Inset * const nextinset = nextInset();
399         // FIXME: the check for asInsetMath() shouldn't be necessary
400         // but math insets do not return a sensible editable() state yet.
401         if (nextinset && !nextinset->asInsetMath()
402             && !nextinset->editable()) {
403                 ++top().pos();
404                 return;
405         }
406         forwardPos();
407 }
408
409
410 void DocIterator::forwardPar()
411 {
412         forwardPos();
413
414         while (!empty() && (!inTexted() || pos() != 0)) {
415                 if (inTexted()) {
416                         pos_type const lastp = lastpos();
417                         Paragraph const & par = paragraph();
418                         pos_type & pos = top().pos();
419                         if (par.insetList().empty())
420                                 pos = lastp;
421                         else
422                                 while (pos < lastp && !par.isInset(pos))
423                                         ++pos;
424                 }
425                 forwardPos();
426         }
427 }
428
429
430 void DocIterator::forwardChar()
431 {
432         forwardPos();
433         while (!empty() && pos() == lastpos())
434                 forwardPos();
435 }
436
437
438 void DocIterator::forwardInset()
439 {
440         forwardPos();
441
442         while (!empty() && !nextInset()) {
443                 if (inTexted()) {
444                         pos_type const lastp = lastpos();
445                         Paragraph const & par = paragraph();
446                         pos_type & pos = top().pos();
447                         while (pos < lastp && !par.isInset(pos))
448                                 ++pos;
449                         if (pos < lastp)
450                                 break;
451                 }
452                 forwardPos();
453         }
454 }
455
456
457 void DocIterator::backwardChar()
458 {
459         backwardPos();
460         while (!empty() && pos() == lastpos())
461                 backwardPos();
462 }
463
464
465 void DocIterator::backwardPos()
466 {
467         //this dog bites his tail
468         if (empty()) {
469                 push_back(CursorSlice(*inset_));
470                 top().idx() = lastidx();
471                 top().pit() = lastpit();
472                 top().pos() = lastpos();
473                 return;
474         }
475
476         // at inset beginning?
477         if (top().at_begin()) {
478                 pop_back();
479                 return;
480         }
481
482         top().backwardPos();
483
484         // entered another cell/paragraph from the right?
485         if (top().pos() == top().lastpos())
486                 return;
487
488         // move into an inset to the left if possible
489         Inset * n = 0;
490         if (inMathed())
491                 n = (top().cell().begin() + top().pos())->nucleus();
492         else
493                 n = paragraph().getInset(top().pos());
494         if (n && n->isActive()) {
495                 push_back(CursorSlice(*n));
496                 top().idx() = lastidx();
497                 top().pit() = lastpit();
498                 top().pos() = lastpos();
499         }
500 }
501
502
503 #if 0
504 // works, but currently not needed
505 void DocIterator::backwardInset()
506 {
507         backwardPos();
508
509         while (!empty() && !nextInset()) {
510                 if (inTexted()) {
511                         pos_type const lastp = lastpos();
512                         Paragraph const & par = paragraph();
513                         pos_type & pos = top().pos();
514                         while (pos > 0 && (pos == lastp || !par.isInset(pos)))
515                                 --pos;
516                         if (pos > 0)
517                                 break;
518                 }
519                 backwardPos();
520         }
521 }
522 #endif
523
524
525 bool DocIterator::hasPart(DocIterator const & it) const
526 {
527         // it can't be a part if it is larger
528         if (it.depth() > depth())
529                 return false;
530
531         // as inset adresses are the 'last' level
532         return &it.top().inset() == &slices_[it.depth() - 1].inset();
533 }
534
535
536 bool DocIterator::allowSpellCheck() const
537 {
538         /// spell check is disabled if the iterator position
539         /// is inside of an inset which disables the spell checker
540         size_t const n = depth();
541         for (size_t i = 0; i < n; ++i) {
542                 if (!slices_[i].inset_->allowSpellCheck())
543                         return false;
544         }
545         return true;
546 }
547
548
549 void DocIterator::updateInsets(Inset * inset)
550 {
551         // this function re-creates the cache of inset pointers.
552         //lyxerr << "converting:\n" << *this << endl;
553         DocIterator dit = *this;
554         size_t const n = slices_.size();
555         slices_.resize(0);
556         for (size_t i = 0 ; i < n; ++i) {
557                 LBUFERR(inset);
558                 push_back(dit[i]);
559                 top().inset_ = inset;
560                 if (i + 1 != n)
561                         inset = nextInset();
562         }
563         //lyxerr << "converted:\n" << *this << endl;
564 }
565
566
567 bool DocIterator::fixIfBroken()
568 {
569         if (empty())
570                 return false;
571
572         // Go through the slice stack from the bottom.
573         // Check that all coordinates (idx, pit, pos) are correct and
574         // that the inset is the one which is claimed to be there
575         Inset * inset = &slices_[0].inset();
576         size_t i = 0;
577         size_t n = slices_.size();
578         for (; i != n; ++i) {
579                 CursorSlice & cs = slices_[i];
580                 if (&cs.inset() != inset || ! cs.inset().isActive()) {
581                         // the whole slice is wrong, chop off this as well
582                         --i;
583                         LYXERR(Debug::DEBUG, "fixIfBroken(): inset changed");
584                         break;
585                 } else if (cs.idx() > cs.lastidx()) {
586                         cs.idx() = cs.lastidx();
587                         cs.pit() = cs.lastpit();
588                         cs.pos() = cs.lastpos();
589                         LYXERR(Debug::DEBUG, "fixIfBroken(): idx fixed");
590                         break;
591                 } else if (cs.pit() > cs.lastpit()) {
592                         cs.pit() = cs.lastpit();
593                         cs.pos() = cs.lastpos();
594                         LYXERR(Debug::DEBUG, "fixIfBroken(): pit fixed");
595                         break;
596                 } else if (cs.pos() > cs.lastpos()) {
597                         cs.pos() = cs.lastpos();
598                         LYXERR(Debug::DEBUG, "fixIfBroken(): pos fixed");
599                         break;
600                 } else if (i != n - 1 && cs.pos() != cs.lastpos()) {
601                         // get inset which is supposed to be in the next slice
602                         if (cs.inset().inMathed())
603                                 inset = (cs.cell().begin() + cs.pos())->nucleus();
604                         else if (Inset * csInset = cs.paragraph().getInset(cs.pos()))
605                                 inset = csInset;
606                         else {
607                                 // there are slices left, so there must be another inset
608                                 break;
609                         }
610                 }
611         }
612
613         // Did we make it through the whole slice stack? Otherwise there
614         // was a problem at slice i, and we have to chop off above
615         if (i < n) {
616                 LYXERR(Debug::DEBUG, "fixIfBroken(): cursor chopped at " << i);
617                 resize(i + 1);
618                 return true;
619         } else
620                 return false;
621 }
622
623
624 void DocIterator::sanitize()
625 {
626         // keep a copy of the slices
627         vector<CursorSlice> const sl = slices_;
628         slices_.clear();
629         if (buffer_)
630                 inset_ = &buffer_->inset();
631         Inset * inset = inset_;
632         // re-add the slices one by one, and adjust the inset pointer.
633         for (size_t i = 0, n = sl.size(); i != n; ++i) {
634                 if (inset == 0) {
635                         // FIXME
636                         LYXERR0(" Should not happen, but does e.g. after "
637                                 "C-n C-l C-z S-C-z\n"
638                                 << " or when a Buffer has been concurrently edited by two views"
639                                 << '\n' << "dit: " << *this << '\n'
640                                 << " lastpos: " << slices_[i].lastpos());
641                         fixIfBroken();
642                         break;
643                 }
644                 if (!inset->isActive()) {
645                         LYXERR0("Inset found on cursor stack is not active.");
646                         fixIfBroken();
647                         break;
648                 }
649                 push_back(sl[i]);
650                 top().inset_ = inset;
651                 if (fixIfBroken())
652                         break;
653                 if (i + 1 != n)
654                         inset = nextInset();
655         }
656 }
657
658
659 int DocIterator::find(MathData const & cell) const
660 {
661         for (size_t l = 0; l != slices_.size(); ++l) {
662                 if (slices_[l].asInsetMath() && &slices_[l].cell() == &cell)
663                         return l;
664         }
665         return -1;
666 }
667
668
669 int DocIterator::find(Inset const * inset) const
670 {
671         for (size_t l = 0; l != slices_.size(); ++l) {
672                 if (&slices_[l].inset() == inset)
673                         return l;
674         }
675         return -1;
676 }
677
678
679 void DocIterator::cutOff(int above, vector<CursorSlice> & cut)
680 {
681         cut = vector<CursorSlice>(slices_.begin() + above + 1, slices_.end());
682         slices_.resize(above + 1);
683 }
684
685
686 void DocIterator::cutOff(int above)
687 {
688         slices_.resize(above + 1);
689 }
690
691
692 void DocIterator::append(vector<CursorSlice> const & x)
693 {
694         slices_.insert(slices_.end(), x.begin(), x.end());
695 }
696
697
698 void DocIterator::append(DocIterator::idx_type idx, pos_type pos)
699 {
700         slices_.push_back(CursorSlice());
701         top().idx() = idx;
702         top().pos() = pos;
703 }
704
705
706 docstring DocIterator::getPossibleLabel() const
707 {
708         return inMathed() ? from_ascii("eq:") : text()->getPossibleLabel(*this);
709 }
710
711
712 Encoding const * DocIterator::getEncoding() const
713 {
714         if (empty())
715                 return 0;
716
717         BufferParams const & bp = buffer()->params();
718         if (bp.useNonTeXFonts)
719                 return encodings.fromLyXName("utf8-plain");
720
721         CursorSlice const & sl = innerTextSlice();
722         Text const & text = *sl.text();
723         Language const * lang =
724                 text.getPar(sl.pit()).getFont(bp, sl.pos(),
725                                                                                   text.outerFont(sl.pit())).language();
726         // If we have a custom encoding for the buffer, we only switch
727         // encoding for CJK (see output_latex::switchEncoding())
728         bool const customenc =
729                 bp.inputenc != "auto" && bp.inputenc != "default";
730         Encoding const * enc =
731                 (customenc && lang->encoding()->package() != Encoding::CJK)
732                 ? &bp.encoding() : lang->encoding();
733
734         // Some insets force specific encodings sometimes (e.g., listings in
735         // multibyte context forces singlebyte).
736         if (inset().forcedEncoding(enc, encodings.fromLyXName("iso8859-1"))) {
737                 // Get the language outside the inset
738                 size_t const n = depth();
739                 for (size_t i = 0; i < n; ++i) {
740                         Text const & otext = *slices_[i].text();
741                         Language const * olang =
742                                         otext.getPar(slices_[i].pit()).getFont(bp, slices_[i].pos(),
743                                                                                                                    otext.outerFont(slices_[i].pit())).language();
744                         Encoding const * oenc = olang->encoding();
745                         if (oenc->name() != "inherit")
746                                 return inset().forcedEncoding(enc, oenc);
747                 }
748                 // Fall back to buffer encoding if no outer lang was found.
749                 return inset().forcedEncoding(enc, &bp.encoding());
750         }
751
752         // Inherited encoding (latex_language) is determined by the context
753         // Look for the first outer encoding that is not itself "inherit"
754         if (lang->encoding()->name() == "inherit") {
755                 size_t const n = depth();
756                 for (size_t i = 0; i < n; ++i) {
757                         Text const & otext = *slices_[i].text();
758                         Language const * olang =
759                                         otext.getPar(slices_[i].pit()).getFont(bp, slices_[i].pos(),
760                                                                                                                    otext.outerFont(slices_[i].pit())).language();
761                         // Again, if we have a custom encoding, this is used
762                         // instead of the language's.
763                         Encoding const * oenc =
764                                         (customenc && olang->encoding()->package() != Encoding::CJK)
765                                         ? &bp.encoding() : olang->encoding();
766                         if (olang->encoding()->name() != "inherit")
767                                 return oenc;
768                 }
769         }
770
771         return enc;
772 }
773
774
775 ostream & operator<<(ostream & os, DocIterator const & dit)
776 {
777         for (size_t i = 0, n = dit.depth(); i != n; ++i)
778                 os << " " << dit[i] << "\n";
779         return os;
780 }
781
782
783 ///////////////////////////////////////////////////////
784
785 StableDocIterator::StableDocIterator(DocIterator const & dit) :
786         data_(dit.internalData())
787 {
788         for (size_t i = 0, n = data_.size(); i != n; ++i)
789                 data_[i].inset_ = 0;
790 }
791
792
793 DocIterator StableDocIterator::asDocIterator(Buffer * buf) const
794 {
795         DocIterator dit(buf);
796         dit.slices_ = data_;
797         dit.sanitize();
798         return dit;
799 }
800
801
802 ostream & operator<<(ostream & os, StableDocIterator const & dit)
803 {
804         for (size_t i = 0, n = dit.data_.size(); i != n; ++i)
805                 os << " " << dit.data_[i] << "\n";
806         return os;
807 }
808
809
810 bool operator==(StableDocIterator const & dit1, StableDocIterator const & dit2)
811 {
812         return dit1.data_ == dit2.data_;
813 }
814
815
816 } // namespace lyx