OSDN Git Service

backup
authorwordring <kouichi_pm@users.osdn.me>
Wed, 5 Aug 2015 13:11:38 +0000 (22:11 +0900)
committerwordring <kouichi_pm@users.osdn.me>
Wed, 5 Aug 2015 13:11:38 +0000 (22:11 +0900)
12 files changed:
proxy/main.cpp
proxy/mainwindow.cpp
proxy/mainwindow.h
proxy/proxy.pro
proxy/textwidget.cpp
proxy/textwidget.h
proxy/tmtext.cpp [new file with mode: 0644]
proxy/tmtext.h [new file with mode: 0644]
proxy/tmwidget.cpp
proxy/tmwidget.h
utility/text.cpp
utility/text.h

index 6463d3c..85a6831 100644 (file)
@@ -32,7 +32,7 @@ int main(int argc, char *argv[])
 
 
        TM::Service *service = new TM::Service(&settings, &a);
-       MainWindow w(service, &settings);
+       MainWindow w(&settings, service);
        w.setFont(QFont("Meiryo", 10.5));
        service->load_languages(QApplication::applicationDirPath() + "/plugins");
 
index a22f539..0d1bbfd 100644 (file)
@@ -7,14 +7,14 @@
 
 #include "debug.h"
 
-MainWindow::MainWindow(TM::Service *service, QSettings *settings, QWidget *parent)
+MainWindow::MainWindow(QSettings *settings, TM::Service *service, QWidget *parent)
        : QMainWindow(parent)
        , m_settings(settings)
 {
        m_editor_dock = new QDockWidget("trans", this);
        m_editor_dock->setObjectName("EditorDock");
        m_editor_dock->setFeatures(QDockWidget::DockWidgetMovable);
-       m_widget = new TM::Widget(service, m_settings, m_editor_dock);
+       m_widget = new TM::Widget(settings, service, m_editor_dock);
        m_editor_dock->setWidget(m_widget);
        addDockWidget(Qt::RightDockWidgetArea, m_editor_dock);
 
index 69d894e..f184974 100644 (file)
@@ -19,7 +19,7 @@ class MainWindow : public QMainWindow
        Q_OBJECT
 
 public:
-       MainWindow(TM::Service *service, QSettings *settings, QWidget *parent = 0);
+       MainWindow(QSettings *settings, TM::Service *service, QWidget *parent = 0);
        ~MainWindow();
 
        TM::Widget* widget();
index fa2168d..ae89011 100644 (file)
@@ -17,14 +17,16 @@ SOURCES += main.cpp\
        tmservice.cpp \
        tmsocket.cpp \
        tmwidget.cpp \
-       textwidget.cpp
+       textwidget.cpp \
+    tmtext.cpp
 
 HEADERS  += mainwindow.h \
        tmhttp.h \
        tmservice.h \
        tmsocket.h \
        tmwidget.h \
-       textwidget.h
+       textwidget.h \
+    tmtext.h
 
 #INCLUDEPATH += $$PWD/../language
 
index 26834c7..6cc7c5d 100644 (file)
@@ -329,6 +329,8 @@ void TextArea::focus_next(TextPanel *panel)
        ensure_visible(tp);
 }
 
+bool TextArea::is_empty() const { return m_panels.isEmpty(); }
+
 void TextArea::resizeEvent(QResizeEvent *ev)
 {
        QWidget::resizeEvent(ev);
index 4b69bad..5172288 100644 (file)
@@ -110,6 +110,8 @@ public:
        void focus_previous(TextPanel *panel);
        void focus_next(TextPanel *panel);
 
+       bool is_empty() const;
+
 signals:
        void focusInChild(TextPanel *new_, TextPanel *old_);
 
diff --git a/proxy/tmtext.cpp b/proxy/tmtext.cpp
new file mode 100644 (file)
index 0000000..dfedf54
--- /dev/null
@@ -0,0 +1,224 @@
+#include "tmtext.h"
+
+#include "debug.h"
+
+// WordLink -------------------------------------------------------------------
+
+TM::WordLink::WordLink() { }
+
+TM::WordLink::WordLink(int place, Text::pointer word)
+{
+       append(place, word);
+}
+
+void TM::WordLink::clear()
+{
+       m_sources.clear();
+       m_targets.clear();
+}
+
+void TM::WordLink::append(int place, Text::pointer word)
+{
+       assert(word);
+       if(place == Source)
+       {
+               if(!m_sources.contains(word)) m_sources.push_back(word);
+       }
+       if(place == Target)
+       {
+               if(!m_targets.contains(word)) m_targets.push_back(word);
+       }
+}
+
+void TM::WordLink::remove(int place, Text::pointer word)
+{
+       assert(word);
+       if(place == Source) m_sources.removeOne(word);
+       if(place == Target) m_targets.removeAll(word);
+}
+
+bool TM::WordLink::is_valid() const
+{
+       return (!m_sources.isEmpty()) && (!m_targets.isEmpty());
+}
+
+bool TM::WordLink::is_empty() const
+{
+       if(!m_sources.isEmpty()) return false;
+       if(!m_targets.isEmpty()) return false;
+       return true;
+}
+
+bool TM::WordLink::contains(int place, Text::pointer word)
+{
+       if(place == Source) return m_sources.contains(word);
+       if(place == Target) return m_targets.contains(word);
+       return false;
+}
+
+int TM::WordLink::size(int place) const
+{
+       if(place == Source) return m_sources.size();
+       if(place == Target) return m_targets.size();
+       return -1;
+}
+
+TM::WordLink::storage_type* TM::WordLink::sources() { return &m_sources; }
+
+TM::WordLink::storage_type* TM::WordLink::targets() { return &m_targets; }
+
+QString TM::WordLink::debug_dump() const
+{
+       QString result;
+       for(Text::pointer p : m_sources) result += p->string() + ",";
+       result += ":";
+       for(Text::pointer p : m_targets) result += p->string() + ",";
+       return result;
+}
+
+TM::WordLink::pointer TM::WordLink::create() { return pointer(new WordLink()); }
+
+TM::WordLink::pointer TM::WordLink::create(int place, Text::pointer word)
+{
+       return pointer(new WordLink(place, word));
+}
+
+// WordLinker -----------------------------------------------------------------
+
+/*!
+ * \brief リンクの編集を開始します。
+ */
+void TM::WordLinker::start()
+{
+       if(!m_current_link) m_current_link = WordLink::create();
+}
+
+/*!
+ * \brief リンクの編集を終了します。
+ */
+void TM::WordLinker::commit()
+{
+       if(!m_current_link) return;
+       if(m_current_link->is_valid()) m_links.append(m_current_link);
+       m_current_link.reset();
+}
+
+/*!
+ * \brief 現在のリンクへ単語を追加します。
+ */
+void TM::WordLinker::append(int place, Text::pointer word)
+{
+       assert(word);
+       if(!m_current_link) start();
+
+       WordLink::pointer p = find(place, word);
+       if(m_current_link->is_empty() && p)
+       {
+               m_current_link = p;
+               m_links.removeOne(p);
+       }
+       else
+       {
+               if(p)
+               {
+                       p->remove(place, word);
+                       if(p->size(place) == 0) m_links.removeOne(p);
+               }
+               m_current_link->append(place, word);
+       }
+}
+
+/*!
+ * \brief 現在のリンクから単語を削除します。
+ */
+void TM::WordLinker::remove(int place, Text::pointer word)
+{
+       assert(word);
+       assert(m_current_link);
+       m_current_link->remove(place, word);
+}
+
+/*!
+ * \brief 現在のリンクから単語の選択状態を反転させます。
+ */
+void TM::WordLinker::toggle(int place, Text::pointer word)
+{
+       assert(word);
+
+       bool contains = false;
+       if(m_current_link) contains = m_current_link->contains(place, word);
+       if(contains) remove(place, word);
+       else append(place, word);
+}
+
+/*!
+ * \brief 現在選択されているリンクを返します。
+ */
+TM::WordLink::pointer TM::WordLinker::current() { return m_current_link; }
+
+/*!
+ * \brief valueを含むWordLinkを検索します。
+ */
+TM::WordLink::pointer TM::WordLinker::find(Text::pointer word)
+{
+       WordLink::pointer p;
+       if(p = find(WordLink::Source, word)) return p;
+       if(p = find(WordLink::Target, word)) return p;
+       return p;
+}
+
+/*!
+ * \brief placeからvalueを含むWordLinkを検索します。
+ */
+TM::WordLink::pointer TM::WordLinker::find(int place, Text::pointer word)
+{
+       for(WordLink::pointer p : m_links) if(p->contains(place, word)) return p;
+       return WordLink::pointer();
+}
+
+int TM::WordLinker::index_of(WordLink::pointer link) const
+{
+       if(m_current_link == link) return 0;
+       int i = m_links.indexOf(link);
+       assert(0 <= i);
+       return (i < 0) ? -1 : i + 1;
+}
+
+TM::WordLinker::iterator TM::WordLinker::begin() { return m_links.begin(); }
+
+TM::WordLinker::iterator TM::WordLinker::end() { return m_links.end(); }
+
+QString TM::WordLinker::debug_dump() const
+{
+       QString result;
+       for(WordLink::pointer p : m_links) result += p->debug_dump() + "|";
+       return result;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/proxy/tmtext.h b/proxy/tmtext.h
new file mode 100644 (file)
index 0000000..2ac7ae2
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef TMTEXT_H
+#define TMTEXT_H
+
+#include "text.h"
+
+#include <QString>
+#include <QList>
+
+#include <memory>
+
+namespace TM
+{
+
+/*!
+ * \brief 単語間のリンクを表現するクラスです。
+ *
+ * 原単語と訳単語の多対多のリンクを表現します。
+ */
+class WordLink
+{
+public:
+       typedef std::shared_ptr<WordLink> pointer;
+       typedef QList<Text::pointer> storage_type;
+
+public:
+       enum : int { Source, Target, };
+
+private:
+       WordLink();
+       WordLink(int place, Text::pointer word);
+
+public:
+       void clear();
+       void append(int place, Text::pointer word);
+       void remove(int place, Text::pointer word);
+
+       bool is_valid() const;
+       bool is_empty() const;
+       bool contains(int place, Text::pointer word);
+       int size(int place) const;
+
+       storage_type* sources();
+       storage_type* targets();
+
+       QString debug_dump() const;
+
+       static pointer create();
+       static pointer create(int place, Text::pointer word);
+
+private:
+       storage_type m_sources;
+       storage_type m_targets;
+};
+
+/*!
+ * \brief 単語間のリンクを作成・保持するクラスです。
+ *
+ * 複数の単語間リンクを保持するコンテナとしての機能を持ちます。
+ * 現在編集中の単語間リンクを明示する機能があります。
+ *
+ * ある特定の単語間リンクを編集するには、start()を呼び出し編集を開始し、
+ * append()呼び出しによってリンクへ単語を追加し、
+ * commit()呼び出しによって編集を終了します。
+ */
+class WordLinker
+{
+public:
+       typedef QList<WordLink::pointer> storage_type;
+       typedef storage_type::iterator iterator;
+       typedef storage_type::const_iterator const_iterator;
+
+public:
+       void start();
+       void commit();
+       void append(int place, Text::pointer word);
+       void remove(int place, Text::pointer word);
+       void toggle(int place, Text::pointer word);
+
+       WordLink::pointer current();
+
+       WordLink::pointer find(Text::pointer word);
+       WordLink::pointer find(int place, Text::pointer word);
+
+       int index_of(WordLink::pointer link) const;
+
+       iterator begin();
+       iterator end();
+
+       QString debug_dump() const;
+
+private:
+       WordLink::pointer m_current_link;
+       storage_type m_links;
+};
+
+} // namespace TM
+
+#endif // TMTEXT_H
index 3dc2095..258ced4 100644 (file)
@@ -1,6 +1,7 @@
 #include "tmwidget.h"
 #include "tmservice.h"
 #include "tmsocket.h"
+#include "tmtext.h"
 
 #include <QSettings>
 #include <QToolBar>
 
 #include <QBrush>
 #include <QFont>
+#include <QMimeData>
+
+#include <QList>
 
 #include <memory>
 
 #include "debug.h"
 
-TM::Widget::Widget(Service *service, QSettings *settings, QWidget *parent)
+TM::Widget::Widget(QSettings *settings, Service *service, QWidget *parent)
        : QWidget(parent)
        , m_mutex(QMutex::Recursive)
        , m_service(service)
@@ -67,7 +71,7 @@ TM::Widget::Widget(Service *service, QSettings *settings, QWidget *parent)
        QAction *setting = new QAction(QIcon(":/setting.png"), "setting", this);
        m_toolbar->addAction(setting);
 
-       m_edit = new Editor(this);
+       m_edit = new Editor(settings, service, this);
        vlayout->addWidget(m_toolbar);
        vlayout->addWidget(m_edit);
 
@@ -82,6 +86,7 @@ void TM::Widget::attach(SocketConnection *socket)
        QMutexLocker lock(&m_mutex);
        m_socket = socket;
        connect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
+       m_link->setDisabled(true);
 }
 
 void TM::Widget::detach(SocketConnection *)
@@ -93,9 +98,15 @@ void TM::Widget::detach(SocketConnection *)
        m_socket = nullptr;
 }
 
+/*!
+ * \brief 編集モードを変更します。
+ */
 void TM::Widget::set_edit_mode(bool mode)
 {
        QMutexLocker lock(&m_mutex);
+       bool old_mode = m_edit_mode->isChecked();
+       if(old_mode == mode) return;
+
        m_edit_mode->setChecked(mode);
 }
 
@@ -105,6 +116,39 @@ bool TM::Widget::edit_mode()
        return m_edit_mode->isChecked();
 }
 
+void TM::Widget::set_link_mode(bool mode)
+{
+       QMutexLocker lock(&m_mutex);
+       bool old_mode = m_link->isChecked();
+       if(old_mode == mode) return;
+
+       m_link->setChecked(mode);
+}
+
+bool TM::Widget::link_mode()
+{
+       QMutexLocker lock(&m_mutex);
+       return m_link->isChecked();
+}
+
+void TM::Widget::disable_link_mode(bool disable)
+{
+       QMutexLocker lock(&m_mutex);
+       m_link->setDisabled(disable);
+}
+
+int TM::Widget::source_language()
+{
+       QMutexLocker lock(&m_mutex);
+       return m_slang->data().toInt();
+}
+
+int TM::Widget::target_language()
+{
+       QMutexLocker lock(&m_mutex);
+       return m_tlang->data().toInt();
+}
+
 void TM::Widget::set_string(QString source_, QString target_)
 {
        QMutexLocker lock(&m_mutex);
@@ -146,10 +190,11 @@ void TM::Widget::onEditModeTriggered(bool)
 {
        QAction *edit_mode = qobject_cast<QAction*>(sender());
        bool checked = edit_mode->isChecked();
-       m_link->setDisabled(!checked);
+       m_link->setDisabled(true);
        m_slang->setDisabled(!checked);
        m_tlang->setDisabled(!checked);
        emit editModeChanged(checked);
+
        m_edit->set_edit_mode(checked);
 }
 
@@ -157,18 +202,6 @@ void TM::Widget::onLinkModeTriggered(bool checked)
 {
        //QAction *link_mode = qobject_cast<QAction*>(sender());
        m_edit->set_link_mode(checked);
-       if(!checked) return;
-       if(TargetPanel *tp = m_edit->current_target_panel())
-       {
-               int code = m_tlang->data().toInt();
-               QString string = tp->toPlainText();
-               Text::pointer sentences = m_service->divide_into_sentences(code, string);
-               if(sentences->size())
-               {
-                       Text::pointer words = m_service->divide_into_words(code, sentences->begin());
-                       tp->set_sentence(words);
-               }
-       }
 }
 
 void TM::Widget::onSourceLanguageTriggered(bool)
@@ -193,10 +226,110 @@ void TM::Widget::onBrowserTriggered(bool)
        QDesktopServices::openUrl(QUrl("http://localhost/"));
 }
 
+// EditorPanel ----------------------------------------------------------------
+
+TM::EditorPanel::EditorPanel(QWidget *parent)
+       : TextPanel(parent)
+       , m_editor(nullptr)
+{
+}
+
+void TM::EditorPanel::set_editor(Editor *editor)
+{
+       assert(!m_editor);
+       m_editor = editor;
+}
+
+Text::pointer TM::EditorPanel::sentence() { return m_sentence; }
+
+void TM::EditorPanel::set_sentence(Text::pointer sentence)
+{
+       assert(sentence);
+       m_sentence = sentence;
+       clear();
+       QTextCursor c = textCursor();
+       for(Text::pointer p = sentence->begin(); p; p = p->next()) // p=word
+       {
+               QTextCharFormat cf;
+               QVariant v = QVariant::fromValue(Text::weak_pointer(p));
+               cf.setProperty(Word, v);
+               c.insertText(p->string(), cf);
+       }
+}
+
+bool TM::EditorPanel::is_empty() const
+{
+       qDebug() << document()->isEmpty();
+       return document()->isEmpty();
+       if(!m_sentence) return true;
+       for(Text::const_pointer p = m_sentence->begin(); p; p = p->next())
+               if(!p->string().isEmpty()) return true;
+       return false;
+}
+
+QTextCursor TM::EditorPanel::select_cursor(Text::pointer word)
+{
+       RangeData *rd = static_cast<RangeData*>(word->data().get());
+       assert(rd);
+       int slength = word->string().length();
+       QTextCursor c = textCursor();
+       c.movePosition(QTextCursor::Start);
+       c.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, rd->begin());
+       c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, slength);
+       return c;
+}
+
+Text::pointer TM::EditorPanel::select_word(QPoint const &pos)
+{
+       QTextCursor c = cursorForPosition(pos);
+       // Textを取得する。
+       QVariant v = c.charFormat().property(Word);
+       if(!v.isValid()) return Text::pointer();
+       Text::weak_pointer wp = v.value<Text::weak_pointer>();
+       assert(!wp.expired());
+       Text::pointer p = wp.lock();
+       return p;
+}
+
+/*!
+ * \brief wordの範囲に背景色colorを設定します。
+ *
+ * 透過色は、Qt::transparentです。
+ */
+void TM::EditorPanel::highlight(Text::pointer word, QColor color)
+{
+       QTextCursor c = select_cursor(word);
+       QTextCharFormat cf = c.charFormat();
+       cf.setBackground(color);
+       c.mergeCharFormat(cf);
+}
+
+void TM::EditorPanel::highlight(WordLink::storage_type *link, QColor color)
+{
+       for(Text::pointer p : *link) highlight(p, color);
+}
+
+void TM::EditorPanel::clear_highlight()
+{
+       if(m_sentence) set_sentence(m_sentence);
+}
+
+/*!
+ * \brief 引数として与えられた整数値から色を作成します。
+ *
+ * 色分け用に使えるQColorを返します。
+ */
+QColor TM::EditorPanel::color(int index) const
+{
+       QColor result;
+       result.setHsv(index * 199, 128, 128, 128);
+       return result;
+}
+
 // SourcePanel ----------------------------------------------------------------
 
 TM::SourcePanel::SourcePanel(QWidget *parent)
-       : TextPanel(parent)
+       : EditorPanel(parent)
        , m_target_panel(nullptr)
 {
        setUndoRedoEnabled(false);
@@ -210,29 +343,49 @@ void TM::SourcePanel::set_target_panel(TargetPanel *target)
        m_target_panel = target;
 }
 
-void TM::SourcePanel::set_sentence(Text::pointer sentence)
+void TM::SourcePanel::commit_link()
 {
-       m_sentence = sentence;
-       clear();
-       QTextCursor c = textCursor();
-       for(Text::pointer p = sentence->begin(); p; p = p->next())
+       m_linker.commit();
+       clear_highlight();
+       m_target_panel->clear_highlight();
+}
+
+TM::WordLinker* TM::SourcePanel::linker()
+{
+       return &m_linker;
+}
+
+void TM::SourcePanel::ensure_highlight()
+{
+       clear_highlight();
+       m_target_panel->clear_highlight();
+
+       WordLink::pointer wl = m_linker.current();
+       if(wl)
        {
-               QTextCharFormat cf;
-               QVariant v = QVariant::fromValue(Text::weak_pointer(p));
-               cf.setProperty(SourceWord, v);
-               c.insertText(p->string(), cf);
+               highlight(wl->sources(), Qt::cyan);
+               target_panel()->highlight(wl->targets(), Qt::cyan);
+       }
+
+       for(WordLink::pointer wl : m_linker)
+       {
+               int index = m_linker.index_of(wl);
+               QColor c = color(index);
+               highlight(wl->sources(), c);
+               target_panel()->highlight(wl->targets(), c);
        }
 }
+
 bool TM::SourcePanel::canInsertFromMimeData(const QMimeData *) const
 {
-       return false; // ドロップ禁止
+       return false; // ドロップ禁止カーソルにする
 }
 
-void TM::SourcePanel::insertFromMimeData(const QMimeData *) { }
+void TM::SourcePanel::insertFromMimeData(const QMimeData *) { } // ドロップされても何もしない
 
 void TM::SourcePanel::inputMethodEvent(QInputMethodEvent *ev)
 {
-       ev->setCommitString("");
+       ev->setCommitString(""); // 入力禁止
        QPlainTextEdit::inputMethodEvent(ev);
 }
 
@@ -241,6 +394,10 @@ void TM::SourcePanel::keyPressEvent(QKeyEvent *ev)
        int key = ev->key();
        switch(key)
        {
+       case Qt::Key_Enter:
+       case Qt::Key_Return:
+               if(m_editor->link_mode()) m_editor->set_link_mode(false);
+               break;
        case Qt::Key_C:
                if(!ev->modifiers().testFlag(Qt::ControlModifier)) break;
        case Qt::Key_Left:
@@ -254,24 +411,17 @@ void TM::SourcePanel::keyPressEvent(QKeyEvent *ev)
 
 void TM::SourcePanel::do_click(QPoint const &pos)
 {
-       QTextCursor c = cursorForPosition(pos);
-       QTextCharFormat cf = c.charFormat();
-       QVariant v = c.charFormat().property(SourceWord);
-       assert(v.isValid());
-       Text::weak_pointer wp = v.value<Text::weak_pointer>();
-       assert(!wp.expired());
-       Text::pointer p = wp.lock();
-       RangeData *rd = static_cast<RangeData*>(p->data().get());
-       assert(rd);
-       c.movePosition(QTextCursor::Start);
-       c.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, rd->begin());
-       qDebug() << wp.lock()->string() << rd->begin() << ":" << rd->tail();
-       int slength = p->string().length();
-       c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, slength);
-       qDebug() << "format size: " << document()->allFormats().size();
-       cf.setBackground(Qt::cyan);
-       c.mergeCharFormat(cf);
-       //setTextCursor(c);
+       if(m_editor->link_mode()) do_click_in_link_mode(pos);
+}
+
+void TM::SourcePanel::do_click_in_link_mode(QPoint const &pos)
+{
+       Text::pointer w = select_word(pos);
+       if(!w) return;
+
+       m_linker.toggle(WordLink::Source, w);
+       ensure_highlight();
+       setTextCursor(cursorForPosition(pos));
 }
 
 void TM::SourcePanel::do_focusin()
@@ -281,41 +431,68 @@ void TM::SourcePanel::do_focusin()
 
 void TM::SourcePanel::do_focusout()
 {
-       //if(m_target_panel) m_target_panel->hide();
 }
 
 // TargetPanel ----------------------------------------------------------------
 
 TM::TargetPanel::TargetPanel(QWidget *parent)
-       : TextPanel(parent)
+       : EditorPanel(parent)
        , m_source_panel(nullptr)
+       , m_text_dirty(false)
 {
        //setContextMenuPolicy(Qt::NoContextMenu);
 }
 
+TM::SourcePanel* TM::TargetPanel::source_panel() { return m_source_panel; }
+
 void TM::TargetPanel::set_source_panel(SourcePanel *source)
 {
        m_source_panel = source;
 }
 
-Text::pointer TM::TargetPanel::sentence() { return m_sentence; }
-
 void TM::TargetPanel::set_sentence(Text::pointer sentence)
 {
-       assert(sentence);
-       m_sentence = sentence;
-       clear();
-       QTextCursor c = textCursor();
-       for(Text::pointer p = sentence->begin(); p; p = p->next())
-       {
-               RangeData *rd = static_cast<RangeData*>(p->data().get());       QTextCursor c = textCursor();
-               qDebug() << rd->begin() << "," << rd->tail() << ": " << p->string();
+       EditorPanel::set_sentence(sentence);
+       set_text_dirty(false);
+}
 
-               QTextCharFormat cf;
-               QVariant v = QVariant::fromValue(Text::weak_pointer(p));
-               cf.setProperty(TargetWord, v);
-               c.insertText(p->string(), cf);
-       }
+bool TM::TargetPanel::is_text_dirty() const { return m_text_dirty; }
+
+void TM::TargetPanel::set_text_dirty(bool dirty) { m_text_dirty = dirty; }
+
+bool TM::TargetPanel::canInsertFromMimeData(QMimeData const *source) const
+{
+       if(source->hasText()) return true;
+       return false;
+}
+
+void TM::TargetPanel::inputMethodEvent(QInputMethodEvent *ev)
+{
+       if(m_editor->link_mode()) ev->setCommitString("");
+       if(!ev->commitString().isEmpty()) set_text_dirty(true);
+       QPlainTextEdit::inputMethodEvent(ev);
+}
+
+void TM::TargetPanel::keyPressEvent_(QKeyEvent *ev)
+{
+       if(m_editor->link_mode()) do_key_press_in_link_mode(ev);
+       else QPlainTextEdit::keyPressEvent(ev);
+}
+
+void TM::TargetPanel::do_click(QPoint const &pos)
+{
+       if(m_editor->link_mode()) do_click_in_link_mode(pos);
+}
+
+void TM::TargetPanel::do_click_in_link_mode(QPoint const &pos)
+{
+       Text::pointer w = select_word(pos);
+       if(!w) return;
+
+       WordLinker *wl = m_source_panel->linker();
+       wl->toggle(WordLink::Target, w);
+       m_source_panel->ensure_highlight();
+       setTextCursor(cursorForPosition(pos));
 }
 
 void TM::TargetPanel::do_focusin()
@@ -328,48 +505,92 @@ void TM::TargetPanel::do_focusout()
        //hide();
 }
 
+void TM::TargetPanel::do_key_press_in_link_mode(QKeyEvent *ev)
+{
+       int key = ev->key();
+       switch(key)
+       {
+       case Qt::Key_Enter:
+       case Qt::Key_Return:
+               if(m_editor->link_mode()) m_editor->set_link_mode(false);
+               break;
+       }
+
+}
+
 // Editor ---------------------------------------------------------------------
 
-TM::Editor::Editor(QWidget *parent)
+TM::Editor::Editor(QSettings *settings, Service *service, QWidget *parent)
        : TextWidget(parent)
+       , m_settings(settings)
+       , m_service(service)
        , m_edit_mode(false)
        , m_link_mode(false)
        , m_current_source_panel(nullptr)
 {
        TextArea *ta = text_area();
        connect(ta, SIGNAL(focusInChild(TextPanel*,TextPanel*)), this, SLOT(onFocusInChild(TextPanel*,TextPanel*)));
+}
 
-       /*QObject::connect(ta, &TextArea::focusChanged, [=](TextPanel *new_, TextPanel *old_){
-               if(m_current_editor_panel) m_current_editor_panel->hide();
-               TmpData* td = new_->user_data<TmpData>();
-               if(td) current_editor_panel = td->data;
-               //assert(current_editor_panel);
-               if(current_editor_panel) current_editor_panel->show();
-       });*/
+void TM::Editor::clear()
+{
+       m_sentences.reset();
+       m_current_source_panel = nullptr;
+       TextArea *ta = text_area();
+       ta->clear();
+       set_link_mode(false);
 }
 
 void TM::Editor::set_sentences(Text::pointer sentences)
 {
+       clear();
        m_sentences = sentences;
        TextArea *ta = text_area();
-       ta->clear();
-
        for(Text::pointer s = sentences->begin(); s; s = s->next()) // s: sentence
        {
                SourcePanel *sp = ta->append<SourcePanel>();
                TargetPanel *tp = ta->append<TargetPanel>();
                sp->set_target_panel(tp);
+               sp->set_editor(this);
                tp->set_source_panel(sp);
+               tp->set_editor(this);
                sp->set_sentence(s);
                sp->show();
        }
 }
 
+bool TM::Editor::edit_mode() const { return m_edit_mode; }
+
 void TM::Editor::set_edit_mode(bool mode_) { m_edit_mode = mode_; }
 
+bool TM::Editor::link_mode() const { return m_link_mode; }
+
 void TM::Editor::set_link_mode(bool mode_)
 {
+       if(mode_ == m_link_mode) return; // モードが変化していない場合、何もしない。
+       assert(!mode_ || can_link_mode()); // リンクモードに入るには、条件がある。
+
        m_link_mode = mode_;
+       parent_widget()->set_link_mode(mode_);
+
+       if(mode_) do_link_mode_enter(m_current_source_panel);
+       else if(m_current_source_panel) do_link_mode_leave(m_current_source_panel);
+}
+
+bool TM::Editor::can_link_mode() const
+{
+       if(!m_current_source_panel) return false;
+       if(!edit_mode()) return false;
+       if(current_target_panel()->is_empty()) return false;
+
+       return true;
+}
+
+TM::Widget* TM::Editor::parent_widget()
+{
+       Widget* result = qobject_cast<Widget*>(parentWidget());
+       assert(result);
+       return result;
 }
 
 TM::TargetPanel* TM::Editor::current_target_panel()
@@ -379,25 +600,94 @@ TM::TargetPanel* TM::Editor::current_target_panel()
        return result;
 }
 
-void TM::Editor::onFocusInChild(TextPanel *new_, TextPanel *)
+TM::TargetPanel const* TM::Editor::current_target_panel() const
+{
+       return const_cast<Editor*>(this)->current_target_panel();
+}
+/*
+bool TM::Editor::is_panel_changed(TextPanel *new_) const
 {
-       for(TextPanel *p : *text_area())
-       {
-               TargetPanel *tp = qobject_cast<TargetPanel*>(p);
-               if(tp) tp->hide();
-       }
        SourcePanel *sp = qobject_cast<SourcePanel*>(new_);
-       if(sp)
-       {
-               sp->target_panel()->show();
-               m_current_source_panel = sp;
-       }
+       if(sp) return sp != m_current_source_panel;
        TargetPanel *tp = qobject_cast<TargetPanel*>(new_);
-       if(tp) tp->show();
+       if(tp) return tp->source_panel() != m_current_source_panel;
+       assert(false);
+       return false;
+}
+*/
+void TM::Editor::onFocusInChild(TextPanel *new_, TextPanel *)
+{
+       SourcePanel *old_panel = m_current_source_panel;
+       SourcePanel *new_panel = find_source_panel(new_);
+       assert(new_panel);
+
+       if(old_panel && old_panel != new_panel) do_panel_leave(old_panel);
+       m_current_source_panel = new_panel;
+       if(old_panel != new_panel) do_panel_enter(new_panel);
+}
+
+TM::SourcePanel* TM::Editor::find_source_panel(TextPanel *panel)
+{
+       SourcePanel *sp = qobject_cast<SourcePanel*>(panel);
+       if(sp) return sp;
+       TargetPanel *tp = qobject_cast<TargetPanel*>(panel);
+       if(tp) return tp->source_panel();
+       return nullptr;
+}
+
+/*!
+ * \brief 文を表示するパネルにフォーカスが入るとき呼び出されます。
+ */
+void TM::Editor::do_panel_enter(SourcePanel *panel)
+{
+       TargetPanel *tp = panel->target_panel();
+       assert(tp);
+       tp->show();
+       if(can_link_mode()) parent_widget()->disable_link_mode(false);
+}
+
+/*!
+ * \brief 文を表示するパネルからフォーカスが出るとき呼び出されます。
+ */
+void TM::Editor::do_panel_leave(SourcePanel *panel)
+{
+       TargetPanel *tp = panel->target_panel();
+       assert(tp);
+       tp->hide();
+
+       set_link_mode(false);
+       parent_widget()->disable_link_mode(true);
 }
 
+void TM::Editor::do_link_mode_enter(SourcePanel *panel)
+{
+       assert(panel);
+       TargetPanel *tp = panel->target_panel();
+       assert(tp);
+       int tcode = parent_widget()->target_language();
 
+       if(tp && tp->is_text_dirty())
+       {
+               qDebug() << "dirty";
+               QString string = tp->toPlainText();
+               Text::pointer sentences = m_service->divide_into_sentences(tcode, string);
+               if(sentences->size())
+               {
+                       Text::pointer words = m_service->divide_into_words(tcode, sentences->begin());
+                       tp->set_sentence(words);
+               }
+       }
+
+       panel->ensure_highlight();
+}
 
+void TM::Editor::do_link_mode_leave(SourcePanel *panel)
+{
+       assert(panel);
+       panel->commit_link();
+       panel->clear_highlight();
+       qDebug() << "do_link_mode_leave";
+}
 
 
 
index cfffc25..d0fee62 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef TMWIDGET_H
 #define TMWIDGET_H
 
+#include "tmtext.h"
 #include "textwidget.h"
 #include "html.h"
 #include "language.h"
@@ -12,6 +13,7 @@
 #include <QList>
 #include <QIcon>
 #include <QString>
+#include <QColor>
 
 #include <QMutex>
 
@@ -40,13 +42,19 @@ class Widget : public QWidget
 {
        Q_OBJECT
 public:
-       explicit Widget(Service *service, QSettings *settings, QWidget *parent = 0);
+       Widget(QSettings *settings, Service *service, QWidget *parent = 0);
 
        void attach(SocketConnection *socket);
        void detach(SocketConnection *socket);
 
        void set_edit_mode(bool mode);
        bool edit_mode();
+       void set_link_mode(bool mode);
+       bool link_mode();
+       void disable_link_mode(bool disable);
+
+       int source_language();
+       int target_language();
 
        void set_string(QString source_, QString target_);
 
@@ -77,69 +85,105 @@ private:
        Editor *m_edit;
 };
 
-class SourcePanel : public TextPanel
+class EditorPanel : public TextPanel
 {
        Q_OBJECT
 public:
-       enum : int
-       {
-               SourceWord = QTextCharFormat::UserProperty,
-       };
+       enum : int { Word = QTextFormat::UserProperty };
+
+public:
+       explicit EditorPanel(QWidget *parent);
+       void set_editor(Editor *editor);
+
+       Text::pointer sentence();
+       virtual void set_sentence(Text::pointer sentence);
+
+       bool is_empty() const;
+
+       QTextCursor select_cursor(Text::pointer word);
+       Text::pointer select_word(QPoint const &pos);
+       void highlight(Text::pointer word, QColor color);
+       void highlight(WordLink::storage_type *link, QColor color);
+       void clear_highlight();
+
+       QColor color(int index) const;
+
+protected:
+       Editor *m_editor;
+       Text::pointer m_sentence;
+};
 
+class SourcePanel : public EditorPanel
+{
+       Q_OBJECT
 public:
-       SourcePanel(QWidget *parent);
+       explicit SourcePanel(QWidget *parent);
 
-       Editor* editor();
        TargetPanel* target_panel();
        void set_target_panel(TargetPanel *target);
-       void set_sentence(Text::pointer sentence);
+
+       void commit_link();
+       WordLinker* linker();
+
+       void ensure_highlight();
 
 protected:
-       bool canInsertFromMimeData(const QMimeData * source) const;
-       void insertFromMimeData(const QMimeData * source);
+       bool canInsertFromMimeData(QMimeData const *source) const;
+       void insertFromMimeData(QMimeData const *source);
 
        void inputMethodEvent(QInputMethodEvent *ev);
        void keyPressEvent(QKeyEvent *ev);
 
+       //void do_enter();
        void do_click(QPoint const &pos);
+       void do_click_in_link_mode(QPoint const &pos);
+
        void do_focusin();
        void do_focusout();
 
 private:
        TargetPanel *m_target_panel;
-       Text::pointer m_sentence;
+       WordLinker m_linker;
 };
 
-class TargetPanel: public TextPanel
+class TargetPanel: public EditorPanel
 {
        Q_OBJECT
 public:
-       enum : int
-       {
-               TargetWord = QTextCharFormat::UserProperty,
-       };
-
-public:
-       TargetPanel(QWidget *parent);
+       explicit TargetPanel(QWidget *parent);
 
-       Editor* editor();
+       SourcePanel* source_panel();
        void set_source_panel(SourcePanel *source);
-       Text::pointer sentence();
+
        void set_sentence(Text::pointer sentence);
 
+       bool is_text_dirty() const;
+       void set_text_dirty(bool dirty);
+
 protected:
+       bool canInsertFromMimeData(QMimeData const *source) const;
+
+       void inputMethodEvent(QInputMethodEvent *ev);
+       void keyPressEvent_(QKeyEvent *ev);
+
+       //void do_enter();
+       void do_click(QPoint const &pos);
+       void do_click_in_link_mode(QPoint const &pos);
+
        void do_focusin();
        void do_focusout();
 
+       void do_key_press_in_link_mode(QKeyEvent *ev);
+
 private:
        SourcePanel *m_source_panel;
-       Text::pointer m_sentence;
+       bool m_text_dirty;
 };
 
 class Editor : public TextWidget
 {
        Q_OBJECT
-       enum : int
+/*     enum : int
        {
                TextSourceSentence = QTextCharFormat::UserProperty,
                TextSourceWord,
@@ -148,19 +192,41 @@ class Editor : public TextWidget
                TextCurrentBlock,
                TextCurrentEditor,
        };
+*/
 public:
-       Editor(QWidget *parent);
+       Editor(QSettings *settings, Service *service, QWidget *parent);
 
+       void clear();
        void set_sentences(Text::pointer sentences);
+
+       bool edit_mode() const;
        void set_edit_mode(bool mode_);
+       bool link_mode() const;
        void set_link_mode(bool mode_);
 
+       bool can_link_mode() const;
+
+       Widget* parent_widget();
        TargetPanel* current_target_panel();
+       TargetPanel const* current_target_panel() const;
+
+       //bool is_panel_changed(TextPanel *new_) const;
 
 protected slots:
        void onFocusInChild(TextPanel *new_, TextPanel *old_);
 
+protected:
+       SourcePanel* find_source_panel(TextPanel *panel);
+
+       void do_panel_enter(SourcePanel *panel);
+       void do_panel_leave(SourcePanel *panel);
+       void do_link_mode_enter(SourcePanel *panel);
+       void do_link_mode_leave(SourcePanel *panel);
+
 private:
+       QSettings *m_settings;
+       Service *m_service;
+
        Text::pointer m_sentences;
        bool m_edit_mode; /*!< 編集モードのときtrue */
        bool m_link_mode; /*!< リンクモードのときtrue */
index 1214c88..e54422e 100644 (file)
@@ -16,6 +16,11 @@ Text::Text(weak_pointer parent_, QString const &string_)
 
 Text::Text(Text const &) { assert(false); }
 
+bool Text::is_empty() const
+{
+       return m_string.isEmpty() && !m_begin;
+}
+
 int Text::size()
 {
        int result = 0;
@@ -43,6 +48,7 @@ Text::pointer Text::at(int i)
  * \brief 最初の子を返す。
  */
 Text::pointer Text::begin() { return m_begin; }
+Text::const_pointer Text::begin() const { return m_begin; }
 
 /*!
  * \brief 最後の子を返す。
@@ -61,6 +67,7 @@ Text::pointer Text::tail()
  * \brief 最後の子の次を返す。
  */
 Text::pointer Text::end() { return pointer(); }
+Text::const_pointer Text::end() const { return pointer(); }
 
 /*!
  * \brief 親を返す。
@@ -85,10 +92,11 @@ Text::pointer Text::previous()
  * \brief 次の兄弟を返す。
  */
 Text::pointer Text::next() { return m_next; }
+Text::const_pointer Text::next() const { return m_next; }
+
 /*!
  * \brief 自身のポインタを返す。
  */
-
 Text::pointer Text::self()
 {
        if(m_parent.expired()) return m_previous.lock(); // rootノードの場合、自身を持っている。
@@ -152,6 +160,7 @@ Text::pointer Text::insert_after(pointer position_, QString const &string_)
 }
 
 QString& Text::string() { return m_string; }
+QString const& Text::string() const { return m_string; }
 
 Text& Text::set_string(QString const &string)
 {
index 85a74da..83d1e08 100644 (file)
@@ -25,18 +25,22 @@ private:
        Text(Text const &);
 
 public:
+       bool is_empty() const;
        int size();
        bool contains(pointer value_);
 
        pointer at(int i);
 
        pointer begin();
+       const_pointer begin() const;
        pointer tail();
        pointer end();
+       const_pointer end() const;
 
        pointer parent();
        pointer previous();
        pointer next();
+       const_pointer next() const;
        pointer self();
 
        pointer append(pointer value_);
@@ -47,6 +51,7 @@ public:
        pointer insert_after(pointer position_, QString const &string_);
 
        QString& string();
+       QString const& string() const;
        Text& set_string(QString const &string);
 
        UserData::pointer data();