+
+/////////////////////////////////////////////////////////////////////
+//
+// SpellResultRange
+//
+/////////////////////////////////////////////////////////////////////
+
+class SpellResultRange {
+public:
+ SpellResultRange(FontSpan range, SpellChecker::Result result)
+ : range_(range), result_(result)
+ {}
+ ///
+ FontSpan const & range() const { return range_; }
+ ///
+ void range(FontSpan const & r) { range_ = r; }
+ ///
+ SpellChecker::Result result() const { return result_; }
+ ///
+ void result(SpellChecker::Result r) { result_ = r; }
+ ///
+ bool inside(pos_type pos) const { return range_.inside(pos); }
+ ///
+ bool covered(FontSpan const & r) const
+ {
+ // 1. first of new range inside current range or
+ // 2. last of new range inside current range or
+ // 3. first of current range inside new range or
+ // 4. last of current range inside new range
+ return range_.inside(r.first) || range_.inside(r.last) ||
+ r.inside(range_.first) || r.inside(range_.last);
+ }
+ ///
+ void shift(pos_type pos, int offset)
+ {
+ if (range_.first > pos) {
+ range_.first += offset;
+ range_.last += offset;
+ } else if (range_.last > pos) {
+ range_.last += offset;
+ }
+ }
+private:
+ FontSpan range_ ;
+ SpellChecker::Result result_ ;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// SpellCheckerState
+//
+/////////////////////////////////////////////////////////////////////
+
+class SpellCheckerState {
+public:
+ SpellCheckerState() {
+ needs_refresh_ = true;
+ current_change_number_ = 0;
+ }
+
+ void setRange(FontSpan const fp, SpellChecker::Result state)
+ {
+ eraseCoveredRanges(fp);
+ if (state != SpellChecker::WORD_OK)
+ ranges_.push_back(SpellResultRange(fp, state));
+ }
+
+ void increasePosAfterPos(pos_type pos)
+ {
+ correctRangesAfterPos(pos, 1);
+ needsRefresh(pos);
+ }
+
+ void decreasePosAfterPos(pos_type pos)
+ {
+ correctRangesAfterPos(pos, -1);
+ needsRefresh(pos);
+ }
+
+ void refreshLast(pos_type pos)
+ {
+ if (pos < refresh_.last)
+ refresh_.last = pos;
+ }
+
+ SpellChecker::Result getState(pos_type pos) const
+ {
+ SpellChecker::Result result = SpellChecker::WORD_OK;
+ RangesIterator et = ranges_.end();
+ RangesIterator it = ranges_.begin();
+ for (; it != et; ++it) {
+ if(it->inside(pos)) {
+ return it->result();
+ }
+ }
+ return result;
+ }
+
+ bool needsRefresh() const {
+ return needs_refresh_;
+ }
+
+ SpellChecker::ChangeNumber currentChangeNumber() const {
+ return current_change_number_;
+ }
+
+ void refreshRange(pos_type & first, pos_type & last) const {
+ first = refresh_.first;
+ last = refresh_.last;
+ }
+
+ void needsRefresh(pos_type pos) {
+ if (needs_refresh_ && pos != -1) {
+ if (pos < refresh_.first)
+ refresh_.first = pos;
+ if (pos > refresh_.last)
+ refresh_.last = pos;
+ } else if (pos != -1) {
+ refresh_.first = pos;
+ refresh_.last = pos;
+ }
+ needs_refresh_ = pos != -1;
+ }
+
+ void needsCompleteRefresh(SpellChecker::ChangeNumber change_number) {
+ needs_refresh_ = true;
+ refresh_.first = 0;
+ refresh_.last = -1;
+ current_change_number_ = change_number;
+ }
+
+private:
+ typedef vector<SpellResultRange> Ranges;
+ typedef Ranges::const_iterator RangesIterator;
+ Ranges ranges_;
+ /// the area of the paragraph with pending spell check
+ FontSpan refresh_;
+ bool needs_refresh_;
+ /// spell state cache version number
+ SpellChecker::ChangeNumber current_change_number_;
+
+
+ void eraseCoveredRanges(FontSpan const fp)
+ {
+ Ranges result;
+ RangesIterator et = ranges_.end();
+ RangesIterator it = ranges_.begin();
+ for (; it != et; ++it) {
+ if (!it->covered(fp))
+ result.push_back(SpellResultRange(it->range(), it->result()));
+ }
+ ranges_ = result;
+ }
+
+ void correctRangesAfterPos(pos_type pos, int offset)
+ {
+ RangesIterator et = ranges_.end();
+ Ranges::iterator it = ranges_.begin();
+ for (; it != et; ++it) {
+ it->shift(pos, offset);
+ }
+ }
+
+};
+