浏览代码

Merge branch 'master' of http://git.gitorious.org/fb2edit/lintest

Kandrashin Denis 13 年之前
父节点
当前提交
ad914ceec5
共有 7 个文件被更改,包括 327 次插入85 次删除
  1. 159 0
      source/fb2html.cpp
  2. 48 0
      source/fb2html.h
  3. 62 75
      source/fb2main.cpp
  4. 9 9
      source/fb2main.hpp
  5. 32 1
      source/fb2text.cpp
  6. 9 0
      source/fb2text.hpp
  7. 8 0
      source/fb2tree.cpp

+ 159 - 0
source/fb2html.cpp

@@ -2,6 +2,110 @@
 #include "fb2utils.h"
 #include "fb2text.hpp"
 
+//---------------------------------------------------------------------------
+//  FbTextElement::Scheme
+//---------------------------------------------------------------------------
+
+FbTextElement::Scheme::Scheme()
+{
+    m_types["body"]
+        << Type("image")
+        << Type("title")
+        << Type("epigraph", 0, 0)
+        << Type()
+    ;
+
+    m_types["section"]
+        << Type("title")
+        << Type("epigraph", 0, 0)
+        << Type("image")
+        << Type("annotation")
+        << Type()
+    ;
+
+    m_types["poem"]
+        << Type("title")
+        << Type("epigraph", 0, 0)
+        << Type("stanza", 1, 0)
+        << Type()
+        << Type("text-author", 0, 0)
+        << Type("date")
+    ;
+
+    m_types["stanza"]
+        << Type("title")
+        << Type("subtitle")
+        << Type()
+    ;
+
+    m_types["epigraph"]
+        << Type()
+        << Type("text-author", 0, 0)
+    ;
+
+    m_types["cite"]
+        << Type()
+        << Type("text-author", 0, 0)
+    ;
+}
+
+const FbTextElement::TypeList * FbTextElement::Scheme::operator[](const QString &name) const
+{
+    TypeMap::const_iterator it = m_types.find(name);
+    if (it != m_types.end()) return &it.value();
+    return 0;
+}
+
+//---------------------------------------------------------------------------
+//  FbTextElement::Sublist
+//---------------------------------------------------------------------------
+
+FbTextElement::Sublist::Sublist(const TypeList &list, const QString &name)
+    : m_list(list)
+    , m_pos(list.begin())
+{
+    TypeList::const_iterator empty = list.end();
+    while (m_pos != list.end()) {
+        if (m_pos->name() == name) break;
+        if (m_pos->name().isEmpty()) empty = m_pos;
+        m_pos++;
+    }
+    if (m_pos == list.end()) m_pos = empty;
+}
+
+FbTextElement::Sublist::operator bool() const
+{
+    return m_pos != m_list.end();
+}
+
+bool FbTextElement::Sublist::operator!() const
+{
+    return m_pos == m_list.end();
+}
+
+bool FbTextElement::Sublist::operator <(const QWebElement &element) const
+{
+    const QString name = element.attribute("class");
+    for (TypeList::const_iterator it = m_list.begin(); it != m_list.end(); it++) {
+        if (it->name() == name) return m_pos < it  || element.isNull();
+    }
+    return false;
+}
+
+bool FbTextElement::Sublist::operator >=(const QWebElement &element) const
+{
+    const QString name = element.attribute("class");
+    for (TypeList::const_iterator it = m_list.begin(); it != m_list.end(); it++) {
+        if (it->name() == name) return m_pos >= it || element.isNull();
+    }
+    return false;
+}
+
+bool FbTextElement::Sublist::operator !=(const QWebElement &element) const
+{
+    return element.isNull() || m_pos->name() != element.attribute("class");
+}
+
 //---------------------------------------------------------------------------
 //  FbTextElement
 //---------------------------------------------------------------------------
@@ -22,6 +126,61 @@ void FbTextElement::getChildren(FbElementList &list)
     }
 }
 
+bool FbTextElement::hasScheme() const
+{
+    return subtypes();
+}
+
+const FbTextElement::TypeList * FbTextElement::subtypes() const
+{
+    static Scheme scheme;
+    return scheme[attribute("class").toLower()];
+}
+
+bool FbTextElement::hasSubtype(const QString &style) const
+{
+    if (const TypeList * list = subtypes()) {
+        for (TypeList::const_iterator item = list->begin(); item != list->end(); item++) {
+            if (item->name() == style) return true;
+        }
+    }
+    return false;
+}
+
+FbTextElement::TypeList::const_iterator FbTextElement::subtype(const TypeList &list, const QString &style)
+{
+    for (TypeList::const_iterator item = list.begin(); item != list.end(); item++) {
+        if (item->name() == style) return item;
+    }
+    return list.end();
+}
+
+FbTextElement FbTextElement::insertInside(const QString &style, const QString &html)
+{
+    const TypeList * types = subtypes();
+    if (!types) return FbTextElement();
+
+    Sublist sublist(*types, style);
+    if (!sublist) return FbTextElement();
+
+    FbTextElement child = firstChild();
+    if (sublist < child) {
+        prependInside(html);
+        return firstChild();
+    }
+
+    while (!child.isNull()) {
+        FbTextElement subling = child.nextSibling();
+        if (sublist >= child && sublist != subling) {
+            child.appendOutside(html);
+            return child.nextSibling();
+        }
+        child = subling;
+    }
+
+    return FbTextElement();
+}
+
 QString FbTextElement::location()
 {
     static const QString javascript = FB2::read(":/js/get_location.js").prepend("var element=this;");

+ 48 - 0
source/fb2html.h

@@ -12,11 +12,56 @@ typedef QList<FbTextElement> FbElementList;
 
 class FbTextElement : public QWebElement
 {
+private:
+    class Type
+    {
+    public:
+        Type(const QString &name = QString(), int min=0, int max=1): m_name(name), m_min(min), m_max(max) {}
+        Type(const Type &t): m_name(t.m_name), m_min(t.m_min), m_max(t.m_max) {}
+        const QString & name() const { return m_name; }
+        int min() { return m_min; }
+        int max() { return m_max; }
+    private:
+        const QString m_name;
+        const int m_min;
+        const int m_max;
+    };
+
+    typedef QList<Type> TypeList;
+
+    typedef QMap<QString, TypeList> TypeMap;
+
+    class Scheme
+    {
+    public:
+        explicit Scheme();
+        const TypeList * operator[](const QString &name) const;
+    private:
+        TypeMap m_types;
+    };
+
+    class Sublist
+    {
+    public:
+        Sublist(const TypeList &list, const QString &name);
+        operator bool() const;
+        bool operator !() const;
+        bool operator <(const QWebElement &element) const;
+        bool operator >=(const QWebElement &element) const;
+        bool operator !=(const QWebElement &element) const;
+    private:
+        const TypeList &m_list;
+        TypeList::const_iterator m_pos;
+    };
+
 public:
     FbTextElement() {}
     FbTextElement(const QWebElement &x) : QWebElement(x) {}
     FbTextElement &operator=(const QWebElement &x) { QWebElement::operator=(x); return *this; }
+    FbTextElement insertInside(const QString &style, const QString &html);
     void getChildren(FbElementList &list);
+    bool hasSubtype(const QString &style) const;
+    bool hasScheme() const;
     QString location();
 
 public:
@@ -40,6 +85,9 @@ public:
 public:
     void select();
 
+private:
+    const TypeList *subtypes() const;
+    TypeList::const_iterator subtype(const TypeList &list, const QString &style);
 };
 
 class FbInsertCmd : public QUndoCommand

+ 62 - 75
source/fb2main.cpp

@@ -12,50 +12,37 @@
 #include "fb2head.hpp"
 #include "fb2utils.h"
 
-FbMainWindow::FbMainWindow()
-{
-    init();
-    setCurrentFile();
-    viewText();
-    textFrame->view.load(":blank.fb2");
-}
-
 FbMainWindow::FbMainWindow(const QString &filename, ViewMode mode)
-{
-    init();
-    setCurrentFile(filename);
-    if (mode == FB2) {
-        viewText();
-        textFrame->view.load(filename);
-    } else {
-        viewCode();
-        loadXML(filename);
-    }
-}
-
-void FbMainWindow::init()
+    : QMainWindow()
+    , textFrame(0)
+    , codeEdit(0)
+    , headTree(0)
+    , noteEdit(0)
+    , toolEdit(0)
+    , dockTree(0)
+    , inspector(0)
+    , messageEdit(0)
+    , isSwitched(false)
+    , isUntitled(true)
 {
     connect(qApp, SIGNAL(logMessage(QString)), SLOT(logMessage(QString)));
 
+    setUnifiedTitleAndToolBarOnMac(true);
     setAttribute(Qt::WA_DeleteOnClose);
     setWindowIcon(QIcon(":icon.ico"));
 
-    isUntitled = true;
-
     createActions();
     createStatusBar();
-
-    textFrame = NULL;
-    noteEdit = NULL;
-    codeEdit = NULL;
-    headTree = NULL;
-    toolEdit = NULL;
-    dockTree = NULL;
-    messageEdit = NULL;
-
     readSettings();
 
-    setUnifiedTitleAndToolBarOnMac(true);
+    setCurrentFile(filename);
+    if (mode == FB2) {
+        viewText();
+        textFrame->view.load(filename.isEmpty() ? ":blank.fb2" : filename);
+    } else {
+        viewCode();
+        if (!filename.isEmpty()) loadXML(filename);
+    }
 }
 
 void FbMainWindow::logMessage(const QString &message)
@@ -178,24 +165,22 @@ void FbMainWindow::about()
 
 void FbMainWindow::documentWasModified()
 {
-    bool modified = false;
-    if (codeEdit) modified = codeEdit->isModified();
-    QFileInfo info = windowFilePath();
-    QString title = info.fileName();
-    if (modified) title += QString("[*]");
-    title += appTitle();
-    setWindowTitle(title);
-    setWindowModified(modified);
+    setModified(isSwitched || codeEdit->isModified());
 }
 
 void FbMainWindow::cleanChanged(bool clean)
+{
+    setModified(isSwitched || !clean);
+}
+
+void FbMainWindow::setModified(bool modified)
 {
     QFileInfo info = windowFilePath();
     QString title = info.fileName();
-    if (!clean) title += QString("[*]");
+    if (modified) title += QString("[*]");
     title += appTitle();
     setWindowTitle(title);
-    setWindowModified(!clean);
+    setWindowModified(modified);
 }
 
 void FbMainWindow::createActions()
@@ -320,10 +305,12 @@ void FbMainWindow::createActions()
 
     actionLink = act = new QAction(FbIcon("insert-link"), tr("&Hiperlink"), this);
     menu->addAction(act);
-    act->setEnabled(false);
 
     menu->addSeparator();
 
+    actionBody = act = new QAction(tr("&Body"), this);
+    menu->addAction(act);
+
     actionSection = act = new QAction(FbIcon("insert-object"), tr("&Section"), this);
     menu->addAction(act);
 
@@ -332,22 +319,15 @@ void FbMainWindow::createActions()
 
     actionEpigraph = act = new QAction(tr("&Epigraph"), this);
     menu->addAction(act);
-    act->setEnabled(false);
 
-    actionDescr = act = new QAction(tr("&Annotation"), this);
+    actionAnnot = act = new QAction(tr("&Annotation"), this);
     menu->addAction(act);
-    act->setEnabled(false);
 
     actionSubtitle = act = new QAction(tr("&Subtitle"), this);
     menu->addAction(act);
 
-    actionAuthor = act = new QAction(tr("&Author"), this);
-    menu->addAction(act);
-    act->setEnabled(false);
-
     actionAuthor = act = new QAction(tr("&Cite"), this);
     menu->addAction(act);
-    act->setEnabled(false);
 
     actionPoem = act = new QAction(tr("&Poem"), this);
     menu->addAction(act);
@@ -355,7 +335,10 @@ void FbMainWindow::createActions()
     actionStanza = act = new QAction(tr("&Stanza"), this);
     menu->addAction(act);
 
-    actionBody = act = new QAction(tr("&Body"), this);
+    actionAuthor = act = new QAction(tr("&Author"), this);
+    menu->addAction(act);
+
+    actionDate = act = new QAction(tr("&Date"), this);
     menu->addAction(act);
 
     menuText = menu = menuBar()->addMenu(tr("Fo&rmat"));
@@ -549,41 +532,33 @@ bool FbMainWindow::saveFile(const QString &fileName, const QString &codec)
     }
 
     if (textFrame) {
+        isSwitched = false;
         textFrame->view.save(&file, codec);
         setCurrentFile(fileName);
+        return true;
     }
-    return true;
 
-/*
-    QTextStream out(&file);
-    QApplication::setOverrideCursor(Qt::WaitCursor);
-    out << textFrame->view.toPlainText();
-    QApplication::restoreOverrideCursor();
+    if (codeEdit) {
+        QTextStream out(&file);
+        out << codeEdit->toPlainText();
+        setCurrentFile(fileName);
+        return true;
+    }
 
-    setCurrentFile(fileName);
-    statusBar()->showMessage(tr("File saved"), 2000);
-*/
+    return false;
 }
 
 void FbMainWindow::setCurrentFile(const QString &filename)
 {
-    static int sequenceNumber = 1;
-
-    QString title;
-    isUntitled = filename.isEmpty();
-    if (isUntitled) {
+    if (filename.isEmpty()) {
+        static int sequenceNumber = 1;
         curFile = QString("book%1.fb2").arg(sequenceNumber++);
-        title = curFile;
     } else {
         QFileInfo info = filename;
         curFile = info.canonicalFilePath();
-        title = info.fileName();
     }
-    title += appTitle();
-
-    setWindowModified(false);
     setWindowFilePath(curFile);
-    setWindowTitle(title);
+    setModified(false);
 }
 
 QString FbMainWindow::appTitle() const
@@ -618,6 +593,7 @@ void FbMainWindow::viewCode()
     QByteArray xml;
     if (textFrame) {
         textFrame->view.save(&xml);
+        isSwitched = true;
         load = true;
     }
 
@@ -670,8 +646,15 @@ void FbMainWindow::viewCode()
 void FbMainWindow::viewText()
 {
     if (textFrame && centralWidget() == textFrame) return;
+
+    bool load = false;
     QString xml;
-    if (codeEdit) xml = codeEdit->text();
+    if (codeEdit) {
+        xml = codeEdit->text();
+        isSwitched = true;
+        load = true;
+    }
+
     FB2DELETE(codeEdit);
     FB2DELETE(headTree);
     if (!textFrame) {
@@ -710,10 +693,14 @@ void FbMainWindow::viewText()
     connect(actionLink, SIGNAL(triggered()), textEdit, SLOT(insertLink()));
 
     connect(actionTitle, SIGNAL(triggered()), textPage, SLOT(insertTitle()));
+    connect(actionAnnot, SIGNAL(triggered()), textPage, SLOT(insertAnnot()));
+    connect(actionAuthor, SIGNAL(triggered()), textPage, SLOT(insertAuthor()));
+    connect(actionEpigraph, SIGNAL(triggered()), textPage, SLOT(insertEpigraph()));
     connect(actionSubtitle, SIGNAL(triggered()), textPage, SLOT(insertSubtitle()));
     connect(actionSection, SIGNAL(triggered()), textPage, SLOT(insertSection()));
     connect(actionStanza, SIGNAL(triggered()), textPage, SLOT(insertStanza()));
     connect(actionPoem, SIGNAL(triggered()), textPage, SLOT(insertPoem()));
+    connect(actionDate, SIGNAL(triggered()), textPage, SLOT(insertDate()));
     connect(actionBody, SIGNAL(triggered()), textPage, SLOT(insertBody()));
 
     connect(actionZoomIn, SIGNAL(triggered()), textEdit, SLOT(zoomIn()));
@@ -721,7 +708,7 @@ void FbMainWindow::viewText()
     connect(actionZoomReset, SIGNAL(triggered()), textEdit, SLOT(zoomReset()));
     connect(actionInspect, SIGNAL(triggered()), textFrame, SLOT(showInspector()));
 
-    if (!xml.isEmpty()) textFrame->view.load(curFile, xml);
+    if (load) textFrame->view.load(curFile, xml);
 
     FB2DELETE(toolEdit);
     QToolBar *tool = toolEdit = addToolBar(tr("Edit"));

+ 9 - 9
source/fb2main.hpp

@@ -26,8 +26,7 @@ class FbMainWindow : public QMainWindow
 
 public:
     enum ViewMode { FB2, XML };
-    explicit FbMainWindow();
-    explicit FbMainWindow(const QString &filename, ViewMode mode = FB2);
+    explicit FbMainWindow(const QString &filename = QString(), ViewMode mode = FB2);
 
 protected:
     void closeEvent(QCloseEvent *event);
@@ -67,29 +66,29 @@ private:
     QString appTitle() const;
 
 private:
-    void init();
     void createHead();
     void createTree();
     void createActions();
     void createStatusBar();
     void readSettings();
     void writeSettings();
+    void setModified(bool modified);
     bool maybeSave();
     bool saveFile(const QString &fileName, const QString &codec = QString());
     void setCurrentFile(const QString &fileName = QString());
     FbMainWindow *findFbMainWindow(const QString &fileName);
 
     FbTextFrame *textFrame;
-    QWebInspector *inspector;
+    FbCodeEdit *codeEdit;
     FbHeadView *headTree;
     QTextEdit *noteEdit;
-    QTextEdit *messageEdit;
+    QToolBar *toolEdit;
     QDockWidget *dockTree;
-    FbCodeEdit *codeEdit;
+    QWebInspector *inspector;
+    QTextEdit *messageEdit;
     QString curFile;
     bool isUntitled;
-
-    QToolBar *toolEdit;
+    bool isSwitched;
 
     QMenu
         *menuEdit,
@@ -114,8 +113,9 @@ private:
         *actionTitle,
         *actionEpigraph,
         *actionSubtitle,
-        *actionDescr,
+        *actionAnnot,
         *actionPoem,
+        *actionDate,
         *actionStanza,
         *actionAuthor,
         *actionSection,

+ 32 - 1
source/fb2text.cpp

@@ -10,6 +10,7 @@
 #include <QBoxLayout>
 #include <QDockWidget>
 #include <QFileDialog>
+#include <QInputDialog>
 #include <QMainWindow>
 #include <QNetworkRequest>
 #include <QStyle>
@@ -237,6 +238,34 @@ void FbTextPage::insertStanza()
     }
 }
 
+void FbTextPage::insertAnnot()
+{
+}
+
+void FbTextPage::insertAuthor()
+{
+}
+
+void FbTextPage::insertEpigraph()
+{
+    const QString type = "epigraph";
+    FbTextElement element = current();
+    while (!element.isNull()) {
+        if (element.hasSubtype(type)) {
+            QString html = div("epigraph", p());
+            element = element.insertInside(type, html);
+            QUndoCommand * command = new FbInsertCmd(element);
+            push(command, tr("Insert epigraph"));
+            break;
+        }
+        element = element.parent();
+    }
+}
+
+void FbTextPage::insertDate()
+{
+}
+
 FbTextElement FbTextPage::current()
 {
     return element(location());
@@ -577,9 +606,11 @@ void FbTextEdit::insertNote()
     dlg.exec();
 }
 
-
 void FbTextEdit::insertLink()
 {
+    bool ok;
+    QString text = QInputDialog::getText(this, tr("Insert hyperlink"), tr("URL:"), QLineEdit::Normal, QString(), &ok);
+    if (ok && !text.isEmpty()) execCommand("CreateLink", text);
 }
 
 void FbTextEdit::execCommand(const QString &cmd, const QString &arg)

+ 9 - 0
source/fb2text.hpp

@@ -48,6 +48,11 @@ protected:
           m_timer.start();
      }
 
+     void keyPressEvent(QKeyEvent *event) {
+         if (event->key() == Qt::Key_Escape) return;
+         QWebView::keyPressEvent(event);
+     }
+
 private:
     QTimer m_timer;
     QSize m_size;
@@ -74,10 +79,14 @@ public:
 public slots:
     void insertBody();
     void insertTitle();
+    void insertAnnot();
+    void insertAuthor();
+    void insertEpigraph();
     void insertSubtitle();
     void insertSection();
     void insertPoem();
     void insertStanza();
+    void insertDate();
 
 protected:
     virtual bool acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type);

+ 8 - 0
source/fb2tree.cpp

@@ -552,6 +552,14 @@ void FbTreeView::contextMenu(const QPoint &pos)
         if (!e.hasChild("date")) menu.addAction(actionDate);
     }
 
+    if (e.isDiv("stanza")) {
+        if (!e.hasChild("title")) menu.addAction(actionTitle);
+    }
+
+    if (e.isDiv("epigraph")) {
+        menu.addAction(actionAuthor);
+    }
+
     if (e.isDiv("cite")) {
         menu.addAction(actionAuthor);
     }