+ return d->group_level_ > 0
+ && !d->undostack_.empty()
+ && d->undostack_.top().group_id == d->group_id_;
+}
+
+
+void Undo::recordUndo(CursorData const & cur, UndoKind kind)
+{
+ d->recordUndo(kind, cur, cur.pit(), cur.pit(), cur);
+}
+
+
+void Undo::recordUndo(CursorData const & cur, pit_type from, pit_type to)
+{
+ d->recordUndo(ATOMIC_UNDO, cur, from, to, cur);
+}
+
+
+void Undo::recordUndoInset(CursorData const & cur, Inset const * inset)
+{
+ if (!inset || inset == &cur.inset()) {
+ DocIterator c = cur;
+ c.pop_back();
+ d->recordUndo(ATOMIC_UNDO, c, c.pit(), c.pit(), cur);
+ } else if (inset == cur.nextInset())
+ recordUndo(cur);
+ else
+ LYXERR0("Inset not found, no undo stack added.");
+}
+
+
+void Undo::recordUndoBufferParams(CursorData const & cur)
+{
+ d->recordUndoBufferParams(cur);
+}
+
+
+void Undo::recordUndoFullBuffer(CursorData const & cur)
+{
+ // This one may happen outside of the main undo group, so we
+ // put it in its own subgroup to avoid complaints.
+ beginUndoGroup();
+ d->recordUndo(ATOMIC_UNDO, doc_iterator_begin(&d->buffer_),
+ 0, d->buffer_.paragraphs().size() - 1, cur);
+ d->recordUndoBufferParams(cur);
+ endUndoGroup();
+}
+
+/// UndoGroupHelper class stuff
+
+class UndoGroupHelper::Impl {
+ friend class UndoGroupHelper;
+ set<Buffer *> buffers_;
+};
+
+
+UndoGroupHelper::UndoGroupHelper(Buffer * buf) : d(new UndoGroupHelper::Impl)
+{
+ resetBuffer(buf);
+}
+
+
+UndoGroupHelper::~UndoGroupHelper()
+{
+ for (Buffer * buf : d->buffers_)
+ if (theBufferList().isLoaded(buf) || theBufferList().isInternal(buf))
+ buf->undo().endUndoGroup();
+ delete d;
+}
+
+void UndoGroupHelper::resetBuffer(Buffer * buf)
+{
+ if (buf && d->buffers_.count(buf) == 0) {
+ d->buffers_.insert(buf);
+ buf->undo().beginUndoGroup();
+ }