+ return row.pos() == 0 && pit == paragraphs().begin();
+}
+
+
+void LyXText::getWord(CursorSlice & from, CursorSlice & to,
+ word_location const loc)
+{
+ Paragraph & from_par = *getPar(from);
+ switch (loc) {
+ case lyx::WHOLE_WORD_STRICT:
+ if (from.pos() == 0 || from.pos() == from_par.size()
+ || from_par.isSeparator(from.pos())
+ || from_par.isKomma(from.pos())
+ || from_par.isNewline(from.pos())
+ || from_par.isSeparator(from.pos() - 1)
+ || from_par.isKomma(from.pos() - 1)
+ || from_par.isNewline(from.pos() - 1)) {
+ to = from;
+ return;
+ }
+ // no break here, we go to the next
+
+ case lyx::WHOLE_WORD:
+ // Move cursor to the beginning, when not already there.
+ if (from.pos() && !from_par.isSeparator(from.pos() - 1)
+ && !(from_par.isKomma(from.pos() - 1)
+ || from_par.isNewline(from.pos() - 1)))
+ cursorLeftOneWord(bv()->cursor());
+ break;
+ case lyx::PREVIOUS_WORD:
+ // always move the cursor to the beginning of previous word
+ cursorLeftOneWord(bv()->cursor());
+ break;
+ case lyx::NEXT_WORD:
+ lyxerr << "LyXText::getWord: NEXT_WORD not implemented yet"
+ << endl;
+ break;
+ case lyx::PARTIAL_WORD:
+ break;
+ }
+ to = from;
+ Paragraph & to_par = *getPar(to);
+ while (to.pos() < to_par.size()
+ && !to_par.isSeparator(to.pos())
+ && !to_par.isKomma(to.pos())
+ && !to_par.isNewline(to.pos())
+ && !to_par.isHfill(to.pos())
+ && !to_par.isInset(to.pos()))
+ {
+ ++to.pos();
+ }
+}
+
+
+void LyXText::write(Buffer const & buf, std::ostream & os) const
+{
+ ParagraphList::const_iterator pit = paragraphs().begin();
+ ParagraphList::const_iterator end = paragraphs().end();
+ Paragraph::depth_type dth = 0;
+ for (; pit != end; ++pit)
+ pit->write(buf, os, buf.params(), dth);
+}
+
+
+bool LyXText::read(Buffer const & buf, LyXLex & lex)
+{
+ static Change current_change;
+
+ bool the_end_read = false;
+ ParagraphList::iterator pit = paragraphs().begin();
+ Paragraph::depth_type depth = 0;
+
+ while (lex.isOK()) {
+ lex.nextToken();
+ string token = lex.getString();
+
+ if (token.empty())
+ continue;
+
+ if (in_inset_) {
+
+ if (token == "\\end_inset") {
+ the_end_read = true;
+ break;
+ }
+
+ if (token == "\\end_document") {
+ lex.printError("\\end_document read in inset! Error in document!");
+ return false;
+ }
+
+ } else {
+
+ if (token == "\\end_document") {
+ the_end_read = true;
+ continue;
+ }
+
+ }
+
+ // FIXME: ugly.
+ int unknown = 0;
+
+ if (token == "\\begin_layout") {
+ lex.pushToken(token);
+
+ Paragraph par;
+ par.params().depth(depth);
+ if (buf.params().tracking_changes)
+ par.trackChanges();
+ LyXFont f(LyXFont::ALL_INHERIT, buf.params().language);
+ par.setFont(0, f);
+
+ // insert after
+ if (pit != paragraphs().end())
+ ++pit;
+
+ pit = paragraphs().insert(pit, par);
+
+ // FIXME: goddamn InsetTabular makes us pass a Buffer
+ // not BufferParams
+ ::readParagraph(buf, *pit, lex);
+
+ } else if (token == "\\begin_deeper") {
+ ++depth;
+ } else if (token == "\\end_deeper") {
+ if (!depth) {
+ lex.printError("\\end_deeper: " "depth is already null");
+ } else {
+ --depth;
+ }
+ } else {
+ ++unknown;
+ }
+
+ }
+ return the_end_read;
+}
+
+
+int LyXText::ascent() const
+{
+ return firstRow()->ascent_of_text();
+}
+
+
+int LyXText::descent() const
+{
+ return height - firstRow()->ascent_of_text();
+}
+
+
+int LyXText::cursorX(CursorSlice const & cur) const
+{
+ ParagraphList::iterator pit = getPar(cur);
+ if (pit->rows.empty())
+ return xo_;
+ Row const & row = *pit->getRow(cur.pos());
+ pos_type pos = cur.pos();
+ pos_type cursor_vpos = 0;
+ double x = row.x();
+ double fill_separator = row.fill_separator();
+ double fill_hfill = row.fill_hfill();
+ double fill_label_hfill = row.fill_label_hfill();
+ pos_type const row_pos = row.pos();
+ pos_type const end = row.endpos();
+
+ if (end <= row_pos)
+ cursor_vpos = row_pos;
+ else if (pos >= end)
+ cursor_vpos = isRTL(*pit) ? row_pos : end;
+ else if (pos > row_pos && pos >= end)
+ // Place cursor after char at (logical) position pos - 1
+ cursor_vpos = (bidi.level(pos - 1) % 2 == 0)
+ ? bidi.log2vis(pos - 1) + 1 : bidi.log2vis(pos - 1);
+ else
+ // Place cursor before char at (logical) position pos
+ cursor_vpos = (bidi.level(pos) % 2 == 0)
+ ? bidi.log2vis(pos) : bidi.log2vis(pos) + 1;
+
+ pos_type body_pos = pit->beginOfBody();
+ if (body_pos > 0 &&
+ (body_pos > end || !pit->isLineSeparator(body_pos - 1)))
+ body_pos = 0;
+
+ for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
+ pos_type pos = bidi.vis2log(vpos);
+ if (body_pos > 0 && pos == body_pos - 1) {
+ x += fill_label_hfill
+ + font_metrics::width(pit->layout()->labelsep,
+ getLabelFont(pit));
+ if (pit->isLineSeparator(body_pos - 1))
+ x -= singleWidth(pit, body_pos - 1);
+ }
+
+ if (hfillExpansion(*pit, row, pos)) {
+ x += singleWidth(pit, pos);
+ if (pos >= body_pos)
+ x += fill_hfill;
+ else
+ x += fill_label_hfill;
+ } else if (pit->isSeparator(pos)) {
+ x += singleWidth(pit, pos);
+ if (pos >= body_pos)
+ x += fill_separator;
+ } else
+ x += singleWidth(pit, pos);
+ }
+ return xo_ + int(x);
+}
+
+
+int LyXText::cursorY(CursorSlice const & cur) const
+{
+ Paragraph & par = *getPar(cur);
+ Row & row = *par.getRow(cur.pos());
+ return yo_ + par.y + row.y_offset() + row.baseline();
+}
+
+
+CursorSlice & LyXText::cursor()
+{
+ //lyxerr << "# accessing slice " << findText(this) << endl;
+ if (this != bv()->cursor().text()) {
+ lyxerr << "cursor: " << bv()->cursor()
+ << "\ntext: " << bv()->cursor().text()
+ << "\nthis: " << this << endl;
+ BOOST_ASSERT(false);
+ }
+ return bv()->cursor().current();
+}
+
+
+CursorSlice const & LyXText::cursor() const
+{
+ if (this != bv()->cursor().text()) {
+ lyxerr << "cursor: " << bv()->cursor()
+ << "\ntext: " << bv()->cursor().text()
+ << "\nthis: " << this << endl;
+ BOOST_ASSERT(false);
+ }
+ return bv()->cursor().current();
+}
+
+
+void LyXText::replaceSelection(LCursor & cur)
+{
+ BOOST_ASSERT(this == cur.text());
+ if (cur.selection()) {
+ cutSelection(cur, true, false);
+ cur.update();
+ }
+}
+
+
+// Returns the current font and depth as a message.
+string LyXText::currentState(LCursor & cur)
+{
+ BOOST_ASSERT(this == cur.text());
+ Buffer * buffer = bv()->buffer();
+ Paragraph const & par = cur.paragraph();
+ std::ostringstream os;
+
+ bool const show_change = buffer->params().tracking_changes
+ && cur.pos() != cur.lastpos()
+ && par.lookupChange(cur.pos()) != Change::UNCHANGED;
+
+ if (show_change) {
+ Change change = par.lookupChangeFull(cur.pos());
+ Author const & a = buffer->params().authors().get(change.author);
+ os << _("Change: ") << a.name();
+ if (!a.email().empty())
+ os << " (" << a.email() << ")";
+ if (change.changetime)
+ os << _(" at ") << ctime(&change.changetime);
+ os << " : ";
+ }
+
+ // I think we should only show changes from the default
+ // font. (Asger)
+ LyXFont font = real_current_font;
+ font.reduce(buffer->params().getLyXTextClass().defaultfont());
+
+ // avoid _(...) re-entrance problem
+ string const s = font.stateText(&buffer->params());
+ os << bformat(_("Font: %1$s"), s);
+
+ // os << bformat(_("Font: %1$s"), font.stateText(&buffer->params));
+
+ // The paragraph depth
+ int depth = getDepth();
+ if (depth > 0)
+ os << bformat(_(", Depth: %1$s"), tostr(depth));
+
+ // The paragraph spacing, but only if different from
+ // buffer spacing.
+ Spacing const & spacing = par.params().spacing();
+ if (!spacing.isDefault()) {
+ os << _(", Spacing: ");
+ switch (spacing.getSpace()) {
+ case Spacing::Single:
+ os << _("Single");
+ break;
+ case Spacing::Onehalf:
+ os << _("OneHalf");
+ break;
+ case Spacing::Double:
+ os << _("Double");
+ break;
+ case Spacing::Other:
+ os << _("Other (") << spacing.getValue() << ')';
+ break;
+ case Spacing::Default:
+ // should never happen, do nothing
+ break;
+ }
+ }
+#ifdef DEVEL_VERSION
+ os << _(", Paragraph: ") << par.id();
+ os << _(", Position: ") << cur.pos();
+ Row & row = cur.textRow();
+ os << bformat(_(", Row b:%1$d e:%2$d"), row.pos(), row.endpos());
+ os << _(", Inset: ");
+ InsetOld * inset = par.inInset();
+ if (inset)
+ os << inset << " owner: " << inset->owner();
+ else
+ os << -1;
+#endif
+ return os.str();
+}
+
+
+string LyXText::getPossibleLabel(LCursor & cur) const
+{
+ ParagraphList & plist = paragraphs();
+ ParagraphList::iterator pit = getPar(cur.par());
+
+ LyXLayout_ptr layout = pit->layout();
+
+ if (layout->latextype == LATEX_PARAGRAPH && pit != plist.begin()) {
+ ParagraphList::iterator pit2 = boost::prior(pit);
+
+ LyXLayout_ptr const & layout2 = pit2->layout();
+
+ if (layout2->latextype != LATEX_PARAGRAPH) {
+ pit = pit2;
+ layout = layout2;
+ }
+ }
+
+ string text = layout->latexname().substr(0, 3);
+ if (layout->latexname() == "theorem")
+ text = "thm"; // Create a correct prefix for prettyref
+
+ text += ':';
+ if (layout->latextype == LATEX_PARAGRAPH || lyxrc.label_init_length < 0)
+ text.erase();
+
+ string par_text = pit->asString(*cur.bv().buffer(), false);
+ for (int i = 0; i < lyxrc.label_init_length; ++i) {
+ if (par_text.empty())
+ break;
+ string head;
+ par_text = split(par_text, head, ' ');
+ // Is it legal to use spaces in labels ?
+ if (i > 0)
+ text += '-';
+ text += head;
+ }
+
+ return text;
+}
+
+
+int LyXText::dist(int x, int y) const
+{
+ int xx = 0;
+ int yy = 0;
+
+ if (x < xo_)
+ xx = xo_ - x;
+ else if (x > xo_ + width)
+ xx = x - xo_ - width;
+
+ if (y < yo_ - ascent())
+ yy = yo_ - ascent() - y;
+ else if (y > yo_ + descent())
+ yy = y - yo_ - descent();
+
+ return xx + yy;