Procházet zdrojové kódy

Optimise undo-redo operations

Kandrashin Denis před 13 roky
rodič
revize
491eb9c5fa
6 změnil soubory, kde provedl 124 přidání a 220 odebrání
  1. 34 127
      source/fb2html.cpp
  2. 9 60
      source/fb2html.h
  3. 6 4
      source/fb2main.cpp
  4. 56 12
      source/fb2text.cpp
  5. 11 1
      source/fb2text.hpp
  6. 8 16
      source/fb2tree.cpp

+ 34 - 127
source/fb2html.cpp

@@ -56,131 +56,73 @@ bool Fb2TextElement::isTitle() const
 }
 
 //---------------------------------------------------------------------------
-//  Fb2UndoCommand
+//  Fb2InsertCmd
 //---------------------------------------------------------------------------
 
-QString Fb2UndoCommand::div(const QString &style, const QString &text)
-{
-    return QString("<div class=%1>%2</div>").arg(style).arg(text);
-}
-
-QString Fb2UndoCommand::p(const QString &text)
-{
-    return QString("<p>%1</p>").arg(text);
-}
-
-//---------------------------------------------------------------------------
-//  Fb2AddBodyCmd
-//---------------------------------------------------------------------------
-
-void Fb2AddBodyCmd::undo()
-{
-    m_body.takeFromDocument();
-    m_page.update();
-}
-
-void Fb2AddBodyCmd::redo()
+Fb2InsertCmd::Fb2InsertCmd(const Fb2TextElement &element)
+    : QUndoCommand()
+    , m_element(element)
+    , m_parent(element.previousSibling())
+    , m_inner(false)
 {
-    Fb2TextElement parent = m_page.body();
-    if (m_body.isNull()) {
-        QString html = div("body", div("title", p()) + div("section", div("title", p()) + p()));
-        parent.appendInside(html);
-        m_body = parent.lastChild();
-    } else {
-        parent.appendInside(m_body);
+    if (m_parent.isNull()) {
+        m_parent = m_element.parent();
+        m_inner = true;
     }
-    m_body.select();
-    m_page.update();
 }
 
-//---------------------------------------------------------------------------
-//  Fb2SectionCmd
-//---------------------------------------------------------------------------
-
-void Fb2SectionCmd::redo()
+void Fb2InsertCmd::redo()
 {
-    if (m_child.isNull()) {
-        QString html = div("section", div("title", p()) + p());
-        m_parent.appendInside(html);
-        m_child = m_parent.lastChild();
+    if (m_inner) {
+        m_parent.prependInside(m_element);
     } else {
-        m_parent.appendInside(m_child);
+        m_parent.appendOutside(m_element);
     }
-    m_child.select();
-    m_page.update();
+    m_element.select();
 }
 
-void Fb2SectionCmd::undo()
+void Fb2InsertCmd::undo()
 {
-    m_child.takeFromDocument();
-    Fb2TextElement last = m_parent.lastChild();
-    if (last.isNull()) {
-        m_parent.select();
-    } else {
-        last.select();
-    }
-    m_page.update();
+    m_element.takeFromDocument();
 }
 
 //---------------------------------------------------------------------------
-//  Fb2TitleCmd
+//  Fb2DeleteCmd
 //---------------------------------------------------------------------------
 
-void Fb2TitleCmd::redo()
+Fb2DeleteCmd::Fb2DeleteCmd(const Fb2TextElement &element)
+    : QUndoCommand()
+    , m_element(element)
+    , m_parent(element.previousSibling())
+    , m_inner(false)
 {
-    if (m_title.isNull()) {
-        QString html = div("title", p());
-        m_section.prependInside(html);
-        m_title = m_section.firstChild();
-    } else {
-        m_section.prependInside(m_title);
+    if (m_parent.isNull()) {
+        m_parent = element.parent();
+        m_inner = true;
     }
-    m_section.select();
-    m_page.update();
 }
 
-void Fb2TitleCmd::undo()
+void Fb2DeleteCmd::redo()
 {
-    m_title.takeFromDocument();
-    m_section.select();
-    m_page.update();
+    m_element.takeFromDocument();
 }
 
-//---------------------------------------------------------------------------
-//  Fb2SubtitleCmd
-//---------------------------------------------------------------------------
-
-void Fb2SubtitleCmd::redo()
+void Fb2DeleteCmd::undo()
 {
-    Fb2TextElement element = m_page.element(m_location);
-    if (m_element.isNull()) {
-        QString html = div("subtitle", p());
-        element.appendOutside(html);
+    if (m_inner) {
+        m_parent.prependInside(m_element);
     } else {
-        element.appendOutside(m_element);
+        m_parent.appendOutside(m_element);
     }
-    element = element.nextSibling();
-    m_position = element.location();
-    element.select();
-    m_page.update();
-}
-
-void Fb2SubtitleCmd::undo()
-{
-    Fb2TextElement element = m_page.element(m_position);
-    Fb2TextElement parent = element.parent();
-    m_element = element.takeFromDocument();
-    parent.select();
-    m_page.update();
+    m_element.select();
 }
 
 //---------------------------------------------------------------------------
 //  Fb2MoveUpCmd
 //---------------------------------------------------------------------------
 
-Fb2MoveUpCmd::Fb2MoveUpCmd(Fb2TextPage &page, const Fb2TextElement &element, Fb2UndoCommand *parent)
-    : Fb2UndoCommand(parent)
-    , m_page(page)
+Fb2MoveUpCmd::Fb2MoveUpCmd(const Fb2TextElement &element)
+    : QUndoCommand()
     , m_element(element)
 {
 }
@@ -189,46 +131,11 @@ void Fb2MoveUpCmd::redo()
 {
     Fb2TextElement subling = m_element.previousSibling();
     subling.prependOutside(m_element.takeFromDocument());
-    m_page.update();
 }
 
 void Fb2MoveUpCmd::undo()
 {
     Fb2TextElement subling = m_element.nextSibling();
     subling.appendOutside(m_element.takeFromDocument());
-    m_page.update();
-}
-
-//---------------------------------------------------------------------------
-//  Fb2DeleteCmd
-//---------------------------------------------------------------------------
-
-Fb2DeleteCmd::Fb2DeleteCmd(Fb2TextPage &page, const Fb2TextElement &element, Fb2UndoCommand *parent)
-    : Fb2UndoCommand(parent)
-    , m_element(element)
-    , m_page(page)
-    , m_inner(false)
-{
-    m_parent = element.previousSibling();
-    if (m_parent.isNull()) {
-        m_parent = element.parent();
-        m_inner = true;
-    }
-}
-
-void Fb2DeleteCmd::redo()
-{
-    m_element.takeFromDocument();
-    m_page.update();
-}
-
-void Fb2DeleteCmd::undo()
-{
-    if (m_inner) {
-        m_parent.prependInside(m_element);
-    } else {
-        m_parent.appendOutside(m_element);
-    }
-    m_page.update();
 }
 

+ 9 - 60
source/fb2html.h

@@ -36,89 +36,38 @@ public:
     void select();
 };
 
-class Fb2UndoCommand : public QUndoCommand
+class Fb2InsertCmd : public QUndoCommand
 {
 public:
-    explicit Fb2UndoCommand(QUndoCommand *parent = 0) : QUndoCommand(parent) {}
-protected:
-    static QString div(const QString &style, const QString &text);
-    static QString p(const QString &text = "<br/>");
-};
-
-class Fb2AddBodyCmd : public Fb2UndoCommand
-{
-public:
-    explicit Fb2AddBodyCmd(Fb2TextPage &page, Fb2UndoCommand *parent = 0) : Fb2UndoCommand(parent), m_page(page) {}
+    explicit Fb2InsertCmd(const Fb2TextElement &element);
     virtual void undo();
     virtual void redo();
 private:
-    Fb2TextPage & m_page;
-    Fb2TextElement m_body;
-};
-
-class Fb2SectionCmd : public Fb2UndoCommand
-{
-public:
-    explicit Fb2SectionCmd(Fb2TextPage &page, const Fb2TextElement &element, Fb2UndoCommand *parent = 0)
-        : Fb2UndoCommand(parent), m_page(page), m_parent(element) {}
-    virtual void undo();
-    virtual void redo();
-private:
-    Fb2TextPage & m_page;
     Fb2TextElement m_parent;
-    Fb2TextElement m_child;
-};
-
-class Fb2TitleCmd : public Fb2UndoCommand
-{
-public:
-    explicit Fb2TitleCmd(Fb2TextPage &page, const Fb2TextElement &element, Fb2UndoCommand *parent = 0)
-        : Fb2UndoCommand(parent), m_page(page), m_section(element) {}
-    virtual void undo();
-    virtual void redo();
-private:
-    Fb2TextPage & m_page;
-    Fb2TextElement m_section;
-    Fb2TextElement m_title;
-};
-
-class Fb2SubtitleCmd : public Fb2UndoCommand
-{
-public:
-    explicit Fb2SubtitleCmd(Fb2TextPage &page, const QString &location, Fb2UndoCommand *parent = 0)
-        : Fb2UndoCommand(parent), m_page(page), m_location(location) {}
-    virtual void undo();
-    virtual void redo();
-private:
     Fb2TextElement m_element;
-    Fb2TextPage & m_page;
-    QString m_location;
-    QString m_position;
+    bool m_inner;
 };
 
-class Fb2MoveUpCmd : public Fb2UndoCommand
+class Fb2DeleteCmd : public QUndoCommand
 {
 public:
-    explicit Fb2MoveUpCmd(Fb2TextPage &page, const Fb2TextElement &element, Fb2UndoCommand *parent = 0);
+    explicit Fb2DeleteCmd(const Fb2TextElement &element);
     virtual void undo();
     virtual void redo();
 private:
-    Fb2TextPage & m_page;
     Fb2TextElement m_element;
+    Fb2TextElement m_parent;
+    bool m_inner;
 };
 
-class Fb2DeleteCmd : public Fb2UndoCommand
+class Fb2MoveUpCmd : public QUndoCommand
 {
 public:
-    explicit Fb2DeleteCmd(Fb2TextPage &page, const Fb2TextElement &element, Fb2UndoCommand *parent = 0);
+    explicit Fb2MoveUpCmd(const Fb2TextElement &element);
     virtual void undo();
     virtual void redo();
 private:
     Fb2TextElement m_element;
-    Fb2TextElement m_parent;
-    Fb2TextPage & m_page;
-    QString m_position;
-    bool m_inner;
 };
 
 #endif // FB2HTML_H

+ 6 - 4
source/fb2main.cpp

@@ -690,14 +690,16 @@ void Fb2MainWindow::viewText()
     connect(actionTextSup, SIGNAL(triggered()), textFrame->view.pageAction(QWebPage::ToggleSuperscript), SIGNAL(triggered()));
 
     Fb2TextEdit * textEdit = &(textFrame->view);
-
     connect(actionFind, SIGNAL(triggered()), textEdit, SLOT(find()));
     connect(actionImage, SIGNAL(triggered()), textEdit, SLOT(insertImage()));
     connect(actionNote, SIGNAL(triggered()), textEdit, SLOT(insertNote()));
     connect(actionLink, SIGNAL(triggered()), textEdit, SLOT(insertLink()));
-    connect(actionTitle, SIGNAL(triggered()), textEdit->page(), SLOT(insertTitle()));
-    connect(actionSubtitle, SIGNAL(triggered()), textEdit->page(), SLOT(insertSubtitle()));
-    connect(actionBody, SIGNAL(triggered()), textEdit->page(), SLOT(insertBody()));
+
+    Fb2TextPage * textPage = textEdit->page();
+    connect(actionTitle, SIGNAL(triggered()), textPage, SLOT(insertTitle()));
+    connect(actionSubtitle, SIGNAL(triggered()), textPage, SLOT(insertSubtitle()));
+    connect(actionSection, SIGNAL(triggered()), textPage, SLOT(insertSection()));
+    connect(actionBody, SIGNAL(triggered()), textPage, SLOT(insertBody()));
 
     connect(actionZoomIn, SIGNAL(triggered()), textEdit, SLOT(zoomIn()));
     connect(actionZoomOut, SIGNAL(triggered()), textEdit, SLOT(zoomOut()));

+ 56 - 12
source/fb2text.cpp

@@ -95,6 +95,16 @@ bool Fb2TextPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkReques
     return QWebPage::acceptNavigationRequest(frame, request, type);
 }
 
+QString Fb2TextPage::div(const QString &style, const QString &text)
+{
+    return QString("<div class=%1>%2</div>").arg(style).arg(text);
+}
+
+QString Fb2TextPage::p(const QString &text)
+{
+    return QString("<p>%1</p>").arg(text);
+}
+
 Fb2TextElement Fb2TextPage::body()
 {
     return doc().findFirst("body");
@@ -105,10 +115,10 @@ Fb2TextElement Fb2TextPage::doc()
     return mainFrame()->documentElement();
 }
 
-void Fb2TextPage::insertBody()
+void Fb2TextPage::push(QUndoCommand * command, const QString &text)
 {
-    undoStack()->beginMacro("Append body");
-    undoStack()->push(new Fb2AddBodyCmd(*this));
+    undoStack()->beginMacro(text);
+    undoStack()->push(command);
     undoStack()->endMacro();
 }
 
@@ -118,15 +128,49 @@ void Fb2TextPage::update()
     emit selectionChanged();
 }
 
+void Fb2TextPage::appendSection(const Fb2TextElement &parent)
+{
+    QString html = div("section", div("title", p()) + p());
+    Fb2TextElement element = parent;
+    element.appendInside(html);
+    element = parent.lastChild();
+    QUndoCommand * command = new Fb2InsertCmd(element);
+    push(command, tr("Append section"));
+}
+
+void Fb2TextPage::insertBody()
+{
+    QString html = div("body", div("title", p()) + div("section", div("title", p()) + p()));
+    Fb2TextElement element = body();
+    element.appendInside(html);
+    element = element.lastChild();
+    QUndoCommand * command = new Fb2InsertCmd(element);
+    push(command, tr("Append body"));
+}
+
+void Fb2TextPage::insertSection()
+{
+    Fb2TextElement element = current();
+    while (!element.isNull()) {
+        if (element.isSection() || element.isBody()) {
+            appendSection(element);
+            break;
+        }
+        element = element.parent();
+    }
+}
+
 void Fb2TextPage::insertTitle()
 {
     Fb2TextElement element = current();
     while (!element.isNull()) {
         Fb2TextElement parent = element.parent();
-        if (parent.isSection() && !parent.hasTitle()) {
-            undoStack()->beginMacro("Insert title");
-            undoStack()->push(new Fb2TitleCmd(*this, parent));
-            undoStack()->endMacro();
+        if ((parent.isSection() || parent.isBody()) && !parent.hasTitle()) {
+            QString html = div("title", p());
+            parent.prependInside(html);
+            element = parent.firstChild();
+            QUndoCommand * command = new Fb2InsertCmd(element);
+            push(command, tr("Insert title"));
             break;
         }
         element = parent;
@@ -139,11 +183,11 @@ void Fb2TextPage::insertSubtitle()
     while (!element.isNull()) {
         Fb2TextElement parent = element.parent();
         if (parent.isSection()) {
-            Fb2TextElement previous = element.previousSibling();
-            if (!previous.isNull()) element = previous;
-            undoStack()->beginMacro("Insert subtitle");
-            undoStack()->push(new Fb2SubtitleCmd(*this, element.location()));
-            undoStack()->endMacro();
+            QString html = div("subtitle", p());
+            element.prependOutside(html);
+            element = element.previousSibling();
+            QUndoCommand * command = new Fb2InsertCmd(element);
+            push(command, tr("Insert subtitle"));
             break;
         }
         element = parent;

+ 11 - 1
source/fb2text.hpp

@@ -13,6 +13,7 @@
 QT_BEGIN_NAMESPACE
 class QDockWidget;
 class QToolBar;
+class QUndoCommand;
 class QWebInspector;
 QT_END_NAMESPACE
 
@@ -59,7 +60,7 @@ class Fb2TextPage : public QWebPage
 public:
     explicit Fb2TextPage(QObject *parent = 0);
 
-    void update();
+    void push(QUndoCommand * command, const QString &text = QString());
     Fb2TextElement element(const QString &location);
     Fb2TextElement current();
     QString location();
@@ -68,13 +69,21 @@ public:
     Fb2TextElement body();
     Fb2TextElement doc();
 
+    void appendSection(const Fb2TextElement &parent);
+
 public slots:
     void insertBody();
     void insertTitle();
     void insertSubtitle();
+    void insertSection();
 
 protected:
     virtual bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type);
+
+protected:
+    static QString div(const QString &style, const QString &text);
+    static QString p(const QString &text = "<br/>");
+    void update();
 };
 
 class Fb2TextEdit : public Fb2TextBase
@@ -123,6 +132,7 @@ private slots:
 
 private:
     void execCommand(const QString &cmd, const QString &arg);
+    void exec(QUndoCommand *command);
     Fb2TemporaryFile * file(const QString &name);
     Fb2NoteView & noteView();
     QWebElement body();

+ 8 - 16
source/fb2tree.cpp

@@ -5,6 +5,7 @@
 #include <QApplication>
 #include <QCursor>
 #include <QVBoxLayout>
+#include <QUndoStack>
 #include <QWebFrame>
 #include <QWebPage>
 #include <QTreeView>
@@ -283,10 +284,8 @@ QModelIndex Fb2TreeModel::move(const QModelIndex &index, int dx, int dy)
             owner->insert(brother, to);
             endMoveRows();
 
-            Fb2TextPage & page = *m_view.page();
-            page.undoStack()->beginMacro("Move element");
-            page.undoStack()->push(new Fb2MoveUpCmd(page, brother->element()));
-            page.undoStack()->endMacro();
+            QUndoCommand * command = new Fb2MoveUpCmd(brother->element());
+            m_view.page()->push(command, tr("Move section"));
         } break;
     }
     return result;
@@ -301,10 +300,8 @@ bool Fb2TreeModel::removeRows(int row, int count, const QModelIndex &parent)
     beginRemoveRows(parent, row, last);
     for (int i = last; i >= row; i--) {
         if (Fb2TreeItem * child = owner->takeAt(i)) {
-            Fb2TextPage & page = *m_view.page();
-            page.undoStack()->beginMacro("Delete element");
-            page.undoStack()->push(new Fb2DeleteCmd(page, child->element()));
-            page.undoStack()->endMacro();
+            QUndoCommand * command = new Fb2DeleteCmd(child->element());
+            m_view.page()->push(command, "Delete element");
             delete child;
         }
     }
@@ -390,6 +387,7 @@ Fb2TreeView::Fb2TreeView(Fb2TextEdit &view, QWidget *parent)
     connect(m_view.page(), SIGNAL(loadFinished(bool)), SLOT(updateTree()));
     connect(m_view.page(), SIGNAL(contentsChanged()), SLOT(contentsChanged()));
     connect(m_view.page(), SIGNAL(selectionChanged()), SLOT(selectionChanged()));
+    connect(m_view.page()->undoStack(), SIGNAL(indexChanged(int)), SLOT(contentsChanged()));
     connect(this, SIGNAL(customContextMenuRequested(QPoint)), SLOT(contextMenu(QPoint)));
 
     m_timerSelect.setInterval(1000);
@@ -556,20 +554,14 @@ void Fb2TreeView::insertNode()
 
         Fb2TextElement element = item->element();
         while (!element.isNull()) {
-            if (element.isSection() || element.isBody())
-            {
-                QUndoStack * undoStack = m_view.page()->undoStack();
-                undoStack->beginMacro("Insert section");
-                undoStack->push(new Fb2SectionCmd(*m_view.page(), element));
-                undoStack->endMacro();
-
+            if (element.isSection() || element.isBody()) {
+                m_view.page()->appendSection(element);
                 QModelIndex result = m->append(index, element.lastChild());
                 if (!result.isValid()) return;
                 setCurrentIndex(result);
                 emit QTreeView::currentChanged(result, index);
                 emit QTreeView::activated(result);
                 scrollTo(result);
-
                 break;
             }
             element = element.parent();