mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-25 10:58:52 +00:00
58103cf214
Until now only the color of the text could be changed by using the text properties dialog. This commit allows changing all other properties except for strikethrough. It is possible to also add underlining with the limitation that the changes accumulate. This requires other work but I think that underlining and strikethrough are not so important in mathed and can be refined at a later time. Fixes #12958
398 lines
12 KiB
C++
398 lines
12 KiB
C++
// -*- C++ -*-
|
|
/**
|
|
* \file DocIterator.h
|
|
* This file is part of LyX, the document processor.
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* \author André Pönitz
|
|
*
|
|
* Full author contact details are available in file CREDITS.
|
|
*/
|
|
|
|
#ifndef DOCITERATOR_H
|
|
#define DOCITERATOR_H
|
|
|
|
#include "CursorSlice.h"
|
|
|
|
#include <vector>
|
|
#include <algorithm> // std::min in MSVC 2017
|
|
|
|
namespace lyx {
|
|
|
|
class DocIterator;
|
|
class Encoding;
|
|
class FontSpan;
|
|
class InsetIterator;
|
|
class LyXErr;
|
|
class MathAtom;
|
|
class Paragraph;
|
|
class Text;
|
|
|
|
DocIterator doc_iterator_begin(Buffer const * buf, Inset const * inset = nullptr);
|
|
DocIterator doc_iterator_end(Buffer const * buf, Inset const * inset = nullptr);
|
|
|
|
|
|
class DocIterator
|
|
{
|
|
public:
|
|
///
|
|
DocIterator() = default;
|
|
|
|
// We could be able to get rid of this if only every BufferView were
|
|
// associated to a buffer on construction.
|
|
explicit DocIterator(Buffer *buf)
|
|
: buffer_(buf)
|
|
{}
|
|
|
|
/// access to owning buffer
|
|
Buffer * buffer() const { return buffer_; }
|
|
/// access to owning buffer
|
|
void setBuffer(Buffer * buf) { buffer_ = buf; }
|
|
|
|
/// Clone this for given \p buffer.
|
|
/// \p buffer must be a clone of buffer_.
|
|
DocIterator clone(Buffer * buffer) const;
|
|
|
|
/// access slice at position \p i
|
|
CursorSlice const & operator[](size_t i) const { return slices_[i]; }
|
|
/// access slice at position \p i
|
|
CursorSlice & operator[](size_t i) { return slices_[i]; }
|
|
/// chop a few slices from the iterator
|
|
void resize(size_t i) { slices_.resize(i); }
|
|
|
|
/// is the iterator valid?
|
|
explicit operator bool() const { return !empty(); }
|
|
|
|
/// does this iterator have any content?
|
|
bool empty() const { return slices_.empty(); }
|
|
/// is this the begin position?
|
|
bool atBegin() const { return depth() == 1 && pit() == 0 && pos() == 0; }
|
|
/// is this the end position?
|
|
bool atEnd() const { return slices_.empty(); }
|
|
|
|
/// checks the cursor slices for disabled spell checker insets
|
|
bool allowSpellCheck() const;
|
|
|
|
//
|
|
// access to slice at tip
|
|
//
|
|
/// access to tip
|
|
CursorSlice & top() { return slices_.back(); }
|
|
/// access to tip
|
|
CursorSlice const & top() const { return slices_.back(); }
|
|
/// access to outermost slice
|
|
CursorSlice & bottom() { return slices_.front(); }
|
|
/// access to outermost slice
|
|
CursorSlice const & bottom() const { return slices_.front(); }
|
|
/// how many nested insets do we have?
|
|
size_t depth() const { return slices_.size(); }
|
|
/// the containing inset
|
|
Inset & inset() const { return top().inset(); }
|
|
/// return the cell of the inset this cursor is in
|
|
idx_type idx() const { return top().idx(); }
|
|
/// return the cell of the inset this cursor is in
|
|
idx_type & idx() { return top().idx(); }
|
|
/// return the last possible cell in this inset
|
|
idx_type lastidx() const;
|
|
/// return the paragraph this cursor is in
|
|
pit_type pit() const { return top().pit(); }
|
|
/// return the paragraph this cursor is in
|
|
pit_type & pit() { return top().pit(); }
|
|
/// return the last possible paragraph in this inset
|
|
pit_type lastpit() const;
|
|
/// return the position within the paragraph
|
|
pos_type pos() const { return top().pos(); }
|
|
/// return the position within the paragraph
|
|
pos_type & pos() { return top().pos(); }
|
|
/// return the last position within the paragraph
|
|
pos_type lastpos() const;
|
|
|
|
/// return the number of embedded cells
|
|
size_t nargs() const;
|
|
/// return the number of embedded cells
|
|
size_t ncols() const;
|
|
/// return the number of embedded cells
|
|
size_t nrows() const;
|
|
/// return the grid row of the top cell
|
|
row_type row() const;
|
|
/// return the last row of the top grid
|
|
row_type lastrow() const { return nrows() - 1; }
|
|
/// return the grid column of the top cell
|
|
col_type col() const;
|
|
/// return the last column of the top grid
|
|
col_type lastcol() const { return ncols() - 1; }
|
|
/// the inset just behind the cursor
|
|
/// returns 0 if there is no inset (e.g. normal text)
|
|
Inset * nextInset() const;
|
|
/// the inset just in front of the cursor
|
|
Inset * prevInset() const;
|
|
///
|
|
bool boundary() const { return boundary_; }
|
|
///
|
|
void boundary(bool b) { boundary_ = b; }
|
|
|
|
// the two methods below have been inlined out because of
|
|
// profiling results under linux when opening a document.
|
|
/// are we in mathed?.
|
|
bool inMathed() const
|
|
{ return !empty() && inset().inMathed(); }
|
|
/// are we in texted?.
|
|
bool inTexted() const
|
|
{ return !empty() && !inset().inMathed(); }
|
|
/// are we in regexp-mode ?
|
|
bool inRegexped() const;
|
|
|
|
//
|
|
// math-specific part
|
|
//
|
|
/// return the mathed cell this cursor is in
|
|
MathData & cell() const;
|
|
///
|
|
InsetMath & nextMath();
|
|
///
|
|
InsetMath & prevMath();
|
|
/// the mathatom left of the cursor
|
|
MathAtom & prevAtom() const;
|
|
/// the mathatom right of the cursor
|
|
MathAtom & nextAtom() const;
|
|
|
|
// text-specific part
|
|
//
|
|
/// the paragraph we're in in text mode.
|
|
/// \warning only works within text!
|
|
Paragraph & paragraph() const;
|
|
/// the paragraph we're in in any case.
|
|
/// This method will give the containing paragraph even
|
|
/// if not in text mode (ex: in mathed).
|
|
Paragraph & innerParagraph() const;
|
|
/// return the inner text slice.
|
|
CursorSlice const & innerTextSlice() const;
|
|
// convert a DocIterator into an argument to LFUN_PARAGRAPH_GOTO
|
|
docstring paragraphGotoArgument() const;
|
|
/// returns a DocIterator for the containing text inset
|
|
DocIterator getInnerText() const;
|
|
/// the first and last positions of a word at top cursor slice
|
|
/// \warning only works within text!
|
|
FontSpan locateWord(word_location const loc) const;
|
|
///
|
|
Text * text() const;
|
|
/// the containing inset or the cell, respectively
|
|
Inset * realInset() const;
|
|
///
|
|
Inset * innerInsetOfType(int code) const;
|
|
///
|
|
Text * innerText() const;
|
|
|
|
//
|
|
// elementary moving
|
|
//
|
|
/// move one step backwards
|
|
bool posBackward();
|
|
/// move one step forward
|
|
bool posForward();
|
|
/**
|
|
* move on one logical position, descend into nested insets
|
|
* including collapsed insets
|
|
*/
|
|
void forwardPos();
|
|
/**
|
|
* move on one logical position, descend into nested insets
|
|
* skip collapsed insets
|
|
*/
|
|
void forwardPosIgnoreCollapsed();
|
|
/// move on one physical character or inset
|
|
void forwardChar();
|
|
/// move on one paragraph
|
|
void forwardPar();
|
|
/// move on to the next closest inset
|
|
void forwardInset();
|
|
/// move backward one logical position
|
|
void backwardPos();
|
|
/// move backward one logical position, skip collapsed insets
|
|
void backwardPosIgnoreCollapsed();
|
|
/// move backward one physical character or inset
|
|
void backwardChar();
|
|
/// move backward one paragraph
|
|
void backwardPar();
|
|
/// move backward one inset
|
|
void backwardInset();
|
|
|
|
/// are we some 'extension' (i.e. deeper nested) of the given iterator
|
|
bool hasPart(DocIterator const & it) const;
|
|
|
|
/// output
|
|
friend std::ostream &
|
|
operator<<(std::ostream & os, DocIterator const & cur);
|
|
friend LyXErr & operator<<(LyXErr & os, DocIterator const & it);
|
|
///
|
|
friend bool operator==(DocIterator const &, DocIterator const &);
|
|
friend bool operator<(DocIterator const &, DocIterator const &);
|
|
friend bool operator>(DocIterator const &, DocIterator const &);
|
|
friend bool operator<=(DocIterator const &, DocIterator const &);
|
|
///
|
|
friend class StableDocIterator;
|
|
//protected:
|
|
///
|
|
void clear() { slices_.clear(); }
|
|
///
|
|
void push_back(CursorSlice const & sl) { slices_.push_back(sl); }
|
|
///
|
|
void pop_back() { slices_.pop_back(); }
|
|
/// recompute the inset parts of the cursor from the document data
|
|
void updateInsets(Inset * inset);
|
|
/// fix DocIterator in circumstances that should never happen.
|
|
/// \return true if the DocIterator was fixed.
|
|
bool fixIfBroken();
|
|
/// Repopulate the slices insets from bottom to top. Useful
|
|
/// for stable iterators or Undo data.
|
|
void sanitize();
|
|
///
|
|
bool isInside(Inset const *) const;
|
|
/// make sure we are outside of given inset
|
|
void leaveInset(Inset const & inset);
|
|
|
|
/// find index of CursorSlice with &cell() == &cell (or -1 if not found)
|
|
int find(MathData const & cell) const;
|
|
/// find index of CursorSlice with inset() == inset (or -1 of not found)
|
|
int find(Inset const * inset) const;
|
|
/// cut off CursorSlices with index > above and store cut off slices in cut.
|
|
void cutOff(int above, std::vector<CursorSlice> & cut);
|
|
/// cut off CursorSlices with index > above
|
|
void cutOff(int above);
|
|
/// push CursorSlices on top
|
|
void append(std::vector<CursorSlice> const & x);
|
|
/// push one CursorSlice on top and set its index and position
|
|
void append(idx_type idx, pos_type pos);
|
|
|
|
///
|
|
docstring getPossibleLabel() const;
|
|
|
|
///
|
|
Encoding const * getEncoding() const;
|
|
private:
|
|
friend class InsetIterator;
|
|
friend DocIterator doc_iterator_begin(Buffer const * buf, Inset const * inset);
|
|
friend DocIterator doc_iterator_end(Buffer const * buf, Inset const * inset);
|
|
///
|
|
explicit DocIterator(Buffer * buf, Inset * inset)
|
|
: inset_(inset), buffer_(buf)
|
|
{}
|
|
|
|
/**
|
|
* Normally, when the cursor is at position i, it is painted *before*
|
|
* the character at position i. However, what if we want the cursor
|
|
* painted *after* position i? That's what boundary_ is for: if
|
|
* boundary_==true, the cursor is painted *after* position i-1, instead
|
|
* of before position i.
|
|
*
|
|
* Note 1: Usually, after i-1 or before i are actually the same place!
|
|
* However, this is not the case when i-1 and i are not painted
|
|
* contiguously, and in these cases we sometimes do want to have control
|
|
* over whether to paint before i or after i-1.
|
|
* Some concrete examples of where this happens:
|
|
* a. i-1 at the end of one row, i at the beginning of next row
|
|
* b. in bidi text, at transitions between RTL and LTR or vice versa
|
|
*
|
|
* Note 2: Why i and i-1? Why, if boundary_==false means: *before* i,
|
|
* couldn't boundary_==true mean: *after* i?
|
|
* Well, the reason is this: cursor position is not used only for
|
|
* painting the cursor, but it also affects other things, for example:
|
|
* where the next insertion will be placed (it is inserted at the current
|
|
* position, pushing anything at the current position and beyond forward).
|
|
* Now, when the current position is i and boundary_==true, insertion would
|
|
* happen *before* i. If the cursor, however, were painted *after* i, that
|
|
* would be very unnatural...
|
|
*/
|
|
bool boundary_ = false;
|
|
///
|
|
std::vector<CursorSlice> const & internalData() const { return slices_; }
|
|
///
|
|
std::vector<CursorSlice> slices_;
|
|
///
|
|
Inset * inset_ = nullptr;
|
|
///
|
|
Buffer * buffer_ = nullptr;
|
|
};
|
|
|
|
|
|
inline bool operator==(DocIterator const & di1, DocIterator const & di2)
|
|
{
|
|
return di1.slices_ == di2.slices_;
|
|
}
|
|
|
|
|
|
inline bool operator!=(DocIterator const & di1, DocIterator const & di2)
|
|
{
|
|
return !(di1 == di2);
|
|
}
|
|
|
|
|
|
inline
|
|
bool operator<(DocIterator const & p, DocIterator const & q)
|
|
{
|
|
size_t depth = std::min(p.depth(), q.depth());
|
|
for (size_t i = 0 ; i < depth ; ++i) {
|
|
if (p[i] != q[i])
|
|
return p[i] < q[i];
|
|
}
|
|
return p.depth() < q.depth();
|
|
}
|
|
|
|
|
|
inline
|
|
bool operator>(DocIterator const & p, DocIterator const & q)
|
|
{
|
|
return q < p;
|
|
}
|
|
|
|
|
|
inline
|
|
bool operator<=(DocIterator const & p, DocIterator const & q)
|
|
{
|
|
return !(q < p);
|
|
}
|
|
|
|
|
|
inline
|
|
bool operator>=(DocIterator const & p, DocIterator const & q)
|
|
{
|
|
return !(p < q);
|
|
}
|
|
|
|
|
|
// The difference to a ('non stable') DocIterator is the removed
|
|
// (overwritten by 0...) part of the CursorSlice data items. So this thing
|
|
// is suitable for external storage, but not for iteration as such.
|
|
|
|
class StableDocIterator
|
|
{
|
|
public:
|
|
///
|
|
StableDocIterator() {}
|
|
/// non-explicit intended
|
|
StableDocIterator(const DocIterator & it);
|
|
///
|
|
DocIterator asDocIterator(Buffer * buf) const;
|
|
///
|
|
size_t size() const { return data_.size(); }
|
|
/// return the position within the paragraph
|
|
pos_type pos() const { return data_.back().pos(); }
|
|
/// return the position within the paragraph
|
|
pos_type & pos() { return data_.back().pos(); }
|
|
///
|
|
friend std::ostream &
|
|
operator<<(std::ostream & os, StableDocIterator const & cur);
|
|
///
|
|
friend std::istream &
|
|
operator>>(std::istream & is, StableDocIterator & cur);
|
|
///
|
|
friend bool
|
|
operator==(StableDocIterator const &, StableDocIterator const &);
|
|
private:
|
|
std::vector<CursorSlice> data_;
|
|
};
|
|
|
|
} // namespace lyx
|
|
|
|
#endif // DOCITERATOR_H
|