OSDN Git Service

c281604489ebd9086d8d5eb1cb42cc847fd87f5b
[wordring-tm/wordring-tm.git] / proxy / tmeditorwidget.cpp
1 #include "tmeditorwidget.h"
2 #include "tmservice.h"
3 #include "tmsocket.h"
4 #include "tmtext.h"
5
6 #include "settings.h"
7
8 #include <QToolBar>
9 #include <QAction>
10 #include <QMenu>
11 #include <QIcon>
12 #include <QDesktopServices>
13
14 #include <QVBoxLayout>
15
16 #include <QMutex>
17 #include <QMutexLocker>
18
19 #include <QTextEdit>
20 #include <QTextDocument>
21 #include <QTextCursor>
22 #include <QTextBlock>
23 #include <QTextBlockFormat>
24
25 #include <QBrush>
26 #include <QFont>
27 #include <QMimeData>
28
29 #include <QList>
30
31 #include <memory>
32
33 #include "debug.h"
34
35 TM::EditorWidget::EditorWidget(Settings *settings, Service *service, QWidget *parent)
36         : QWidget(parent)
37         , m_mutex(QMutex::Recursive)
38         , m_service(service)
39         , m_settings(settings)
40         , m_http_port(80)
41         , m_socket(nullptr)
42 {
43         QVBoxLayout *vlayout = new QVBoxLayout(this);
44         vlayout->setSpacing(4);
45         vlayout->setContentsMargins(0, 0, 0, 0);
46
47         m_toolbar = new QToolBar("Editor", this);
48
49         m_edit_mode = new QAction(QIcon(":/edit.png"), "edit", this);
50         m_edit_mode->setCheckable(true);
51         m_toolbar->addAction(m_edit_mode);
52         connect(m_edit_mode, SIGNAL(triggered(bool)), this, SLOT(onEditModeTriggered(bool)));
53
54         m_link = new QAction(QIcon(":/link.png"), "link", this);
55         m_link->setCheckable(true);
56         m_link->setDisabled(true);
57         m_toolbar->addAction(m_link);
58         connect(m_link, SIGNAL(triggered(bool)), this, SLOT(onLinkModeTriggered(bool)));
59
60         m_slang = new QAction("&Source", this);
61         m_slang->setDisabled(true);
62         m_slang->setMenu(new QMenu(this));
63         m_tlang = new QAction("&Target", this);
64         m_tlang->setDisabled(true);
65         m_tlang->setMenu(new QMenu(this));
66         m_toolbar->addAction(m_slang);
67         m_toolbar->addAction(m_tlang);
68
69         QAction *browser = new QAction(QIcon(":/browser.png"), "browser", this);
70         m_toolbar->addAction(browser);
71         connect(browser, SIGNAL(triggered(bool)), this, SLOT(onBrowserTriggered(bool)));
72
73         QAction *setting = new QAction(QIcon(":/setting.png"), "setting", this);
74         m_toolbar->addAction(setting);
75
76         m_edit = new Editor(settings, service, this);
77         vlayout->addWidget(m_toolbar);
78         vlayout->addWidget(m_edit);
79
80         connect(m_service, SIGNAL(languageLoaded(int, QString, QIcon)), this, SLOT(onLanguageLoaded(int,QString,QIcon)));
81 }
82
83 /*!
84  * \brief ブラウザ上でドキュメントがフォーカスを取得したとき、
85  * SocketConnectionから呼び出されます。
86  *
87  * SocketConnectionは別スレッドに在ります。
88  */
89 void TM::EditorWidget::attach(SocketConnection *socket)
90 {
91         if(m_socket == socket) return;
92         if(m_socket) detach(m_socket);
93
94         QMutexLocker lock(&m_mutex);
95         m_socket = socket;
96         connect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
97         set_link_mode_disabled(true);
98 }
99
100 /*!
101  * \brief ブラウザ上でドキュメントがフォーカスを取得することで、
102  * 現在のSocketConnectionがフォーカスを失った時に呼び出されます。
103  *
104  * このメンバはattach()から呼び出されますが、その呼び出し元は別スレッドに在ります。
105  */
106 void TM::EditorWidget::detach(SocketConnection *)
107 {
108         QMutexLocker lock(&m_mutex);
109         disconnect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
110
111         m_edit->clear();
112         m_socket = nullptr;
113 }
114
115 void TM::EditorWidget::set_http_port(quint16 http_port) { m_http_port = http_port; }
116
117 /*!
118  * \brief 編集モードを変更します。
119  */
120 void TM::EditorWidget::set_edit_mode(bool mode)
121 {
122         QMutexLocker lock(&m_mutex);
123         bool old_mode = m_edit_mode->isChecked();
124         if(old_mode == mode) return;
125
126         m_edit_mode->setChecked(mode);
127 }
128
129 /*!
130  * \brief 編集モードの場合trueを返します。
131  */
132 bool TM::EditorWidget::edit_mode()
133 {
134         QMutexLocker lock(&m_mutex);
135         return m_edit_mode->isChecked();
136 }
137
138 /*!
139  * \brief リンクモードを設定します。
140  */
141 void TM::EditorWidget::set_link_mode(bool mode)
142 {
143         QMutexLocker lock(&m_mutex);
144         bool old_mode = m_link->isChecked();
145         if(old_mode == mode) return;
146
147         m_link->setChecked(mode);
148 }
149
150 /*!
151  * \brief リンクモードの場合、trueを返します。
152  */
153 bool TM::EditorWidget::link_mode()
154 {
155         QMutexLocker lock(&m_mutex);
156         return m_link->isChecked();
157 }
158
159 /*!
160  * \brief リンクボタンをグレーアウトします。
161  *
162  *
163  */
164 void TM::EditorWidget::set_link_mode_disabled(bool disable)
165 {
166         QMutexLocker lock(&m_mutex);
167         m_link->setDisabled(disable);
168 }
169
170 /*!
171  * \brief 原文の言語コードを返します。
172  */
173 int TM::EditorWidget::source_language()
174 {
175         QMutexLocker lock(&m_mutex);
176         return m_slang->data().toInt();
177 }
178
179 /*!
180  * \brief 訳文の言語コードを返します。
181  */
182 int TM::EditorWidget::target_language()
183 {
184         QMutexLocker lock(&m_mutex);
185         return m_tlang->data().toInt();
186 }
187
188 void TM::EditorWidget::set_string(QString source_, QString target_)
189 {
190         QMutexLocker lock(&m_mutex);
191         int code = m_slang->data().toInt();
192
193         Text::pointer result = Text::create();
194         Text::pointer sentences = m_service->divide_into_sentences(code, source_);
195         for(Text::pointer s = sentences->begin(); s; s = s->next())
196         {
197                 Text::pointer words = m_service->divide_into_words(code, s);
198                 result->append(words);
199         }
200         m_edit->set_sentences(result);
201 }
202
203 /*!
204  * \brief 言語プラグインが読み込まれるたびに呼び出されます。
205  * \param code 言語コード。
206  * \param name 言語名。
207  * \param icon 言語を表すアイコン。
208  */
209 void TM::EditorWidget::onLanguageLoaded(int code, QString name, QIcon icon)
210 {
211         QString dslanguage = m_settings->value("Widget/defaultSourceLanguage", "English").toString();
212         QString tslanguage = m_settings->value("Widget/defaultSourceLanguage", "Japanese").toString();
213         if(dslanguage == name)
214         {
215                 m_slang->setIcon(icon);
216                 m_slang->setData(code);
217         }
218         if(tslanguage == name)
219         {
220                 m_tlang->setIcon(icon);
221                 m_tlang->setData(code);
222         }
223         QAction *saction =  m_slang->menu()->addAction(icon, name);
224         saction->setData(code);
225         connect(saction, SIGNAL(triggered(bool)), this, SLOT(onSourceLanguageTriggered(bool)));
226         QAction *taction = m_tlang->menu()->addAction(icon, name);
227         taction->setData(code);
228         connect(taction, SIGNAL(triggered(bool)), this, SLOT(onTargetLanguageTriggered(bool)));
229 }
230
231 /*!
232  * \brief 編集アイコンがクリックされたとき呼び出されます。
233  */
234 void TM::EditorWidget::onEditModeTriggered(bool)
235 {
236         QAction *edit_mode = qobject_cast<QAction*>(sender());
237         bool checked = edit_mode->isChecked();
238         m_link->setDisabled(true);
239         m_slang->setDisabled(!checked);
240         m_tlang->setDisabled(!checked);
241         emit editModeChanged(checked);
242
243         m_edit->set_edit_mode(checked);
244 }
245
246 /*!
247  * \brief リンクアイコンがクリックされたとき呼び出されます。
248  */
249 void TM::EditorWidget::onLinkModeTriggered(bool checked)
250 {
251         //QAction *link_mode = qobject_cast<QAction*>(sender());
252         m_edit->set_link_mode(checked);
253 }
254
255 /*!
256  * \brief アクションから原文言語が変更されたとき呼び出されます。
257  */
258 void TM::EditorWidget::onSourceLanguageTriggered(bool)
259 {
260         QAction *saction = qobject_cast<QAction*>(sender());
261         assert(saction);
262         m_slang->setIcon(saction->icon());
263         m_slang->setData(saction->data());
264 }
265
266 /*!
267  * \brief アクションから訳文言語が変更されたとき呼び出されます。
268  */
269 void TM::EditorWidget::onTargetLanguageTriggered(bool)
270 {
271         QAction *taction = qobject_cast<QAction*>(sender());
272         assert(taction);
273         m_tlang->setIcon(taction->icon());
274         m_tlang->setData(taction->data());
275 }
276
277 /*!
278  * \brief ブラウザ・アイコンがクリックされたとき呼び出されます。
279  */
280 void TM::EditorWidget::onBrowserTriggered(bool)
281 {
282         QString url("http://localhost:");
283         url += QString::number(m_http_port) + "/";
284
285         QDesktopServices::openUrl(QUrl(url));
286 }
287
288 // EditorPanel ----------------------------------------------------------------
289
290 TM::EditorPanel::EditorPanel(QWidget *parent)
291         : TextPanel(parent)
292         , m_editor(nullptr)
293 {
294 }
295
296 /*!
297  * \brief 親となるエディタを設定します。
298  */
299 void TM::EditorPanel::set_editor(Editor *editor)
300 {
301         assert(!m_editor);
302         m_editor = editor;
303 }
304
305 /*!
306  * \brief 保持している文を返します。
307  */
308 Text::pointer TM::EditorPanel::sentence() { return m_sentence; }
309
310 /*!
311  * \brief 文を設定します。
312  */
313 void TM::EditorPanel::set_sentence(Text::pointer sentence)
314 {
315         assert(sentence);
316         m_sentence = sentence;
317         clear();
318         QTextCursor c = textCursor();
319         for(Text::pointer p = sentence->begin(); p; p = p->next()) // p=word
320         {
321                 QTextCharFormat cf;
322                 QVariant v = QVariant::fromValue(Text::weak_pointer(p));
323                 cf.setProperty(Word, v);
324                 c.insertText(p->string(), cf);
325         }
326 }
327
328 /*!
329  * \brief 内容が空の場合、trueを返します。
330  */
331 bool TM::EditorPanel::is_empty() const { return document()->isEmpty(); }
332
333 /*!
334  * \brief 引数として与えられた単語に該当する範囲を選択するカーソルを返します。
335  */
336 QTextCursor TM::EditorPanel::select_cursor(Text::pointer word)
337 {
338         RangeData *rd = static_cast<RangeData*>(word->data().get());
339         assert(rd);
340         int slength = word->string().length();
341         QTextCursor c = textCursor();
342         c.movePosition(QTextCursor::Start);
343         c.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, rd->begin());
344         c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, slength);
345         return c;
346 }
347
348 /*!
349  * \brief 引数として与えられた位置にある単語を返します。
350  */
351 Text::pointer TM::EditorPanel::select_word(QPoint const &pos)
352 {
353         QTextCursor c = cursorForPosition(pos);
354         // Textを取得する。
355         QVariant v = c.charFormat().property(Word);
356         if(!v.isValid()) return Text::pointer();
357         Text::weak_pointer wp = v.value<Text::weak_pointer>();
358         assert(!wp.expired());
359         Text::pointer p = wp.lock();
360         return p;
361 }
362
363 /*!
364  * \brief wordの範囲に背景色colorを設定します。
365  *
366  * 透過色は、Qt::transparentです。
367  */
368 void TM::EditorPanel::highlight(Text::pointer word, QColor color)
369 {
370         QTextCursor c = select_cursor(word);
371         QTextCharFormat cf = c.charFormat();
372         cf.setBackground(color);
373         c.mergeCharFormat(cf);
374 }
375
376 void TM::EditorPanel::highlight(WordLink::storage_type *link, QColor color)
377 {
378         for(Text::pointer p : *link) highlight(p, color);
379 }
380
381 void TM::EditorPanel::clear_highlight()
382 {
383         if(m_sentence) set_sentence(m_sentence);
384 }
385
386 /*!
387  * \brief 引数として与えられた整数値から色を作成します。
388  *
389  * 色分け用に使えるQColorを返します。
390  */
391 QColor TM::EditorPanel::color(int index) const
392 {
393         QColor result;
394         result.setHsv(index * 199, 128, 128, 128);
395         return result;
396 }
397
398 // SourcePanel ----------------------------------------------------------------
399
400 TM::SourcePanel::SourcePanel(QWidget *parent)
401         : EditorPanel(parent)
402         , m_target_panel(nullptr)
403 {
404         setUndoRedoEnabled(false);
405         setContextMenuPolicy(Qt::NoContextMenu);
406 }
407
408 TM::TargetPanel* TM::SourcePanel::target_panel() { return m_target_panel; }
409
410 void TM::SourcePanel::set_target_panel(TargetPanel *target)
411 {
412         m_target_panel = target;
413 }
414
415 void TM::SourcePanel::commit_link()
416 {
417         m_linker.commit();
418         clear_highlight();
419         m_target_panel->clear_highlight();
420 }
421
422 TM::WordLinker* TM::SourcePanel::linker()
423 {
424         return &m_linker;
425 }
426
427 /*!
428  * \brief 保持するリンクを色分け表示します。
429  */
430 void TM::SourcePanel::ensure_highlight()
431 {
432         clear_highlight();
433         m_target_panel->clear_highlight();
434
435         WordLink::pointer wl = m_linker.current();
436         if(wl)
437         {
438                 highlight(wl->sources(), Qt::cyan);
439                 target_panel()->highlight(wl->targets(), Qt::cyan);
440         }
441
442         for(WordLink::pointer wl : m_linker)
443         {
444                 int index = m_linker.index_of(wl);
445                 QColor c = color(index);
446                 highlight(wl->sources(), c);
447                 target_panel()->highlight(wl->targets(), c);
448         }
449 }
450
451 bool TM::SourcePanel::canInsertFromMimeData(const QMimeData *) const
452 {
453         return false; // ドロップ禁止カーソルにする
454 }
455
456 void TM::SourcePanel::insertFromMimeData(const QMimeData *) { } // ドロップされても何もしない
457
458 void TM::SourcePanel::inputMethodEvent(QInputMethodEvent *ev)
459 {
460         ev->setCommitString(""); // 入力禁止
461         QPlainTextEdit::inputMethodEvent(ev);
462 }
463
464 void TM::SourcePanel::keyPressEvent(QKeyEvent *ev)
465 {
466         int key = ev->key();
467         switch(key)
468         {
469         case Qt::Key_Enter:
470         case Qt::Key_Return:
471                 if(m_editor->link_mode()) m_editor->set_link_mode(false);
472                 break;
473         case Qt::Key_C:
474                 if(!ev->modifiers().testFlag(Qt::ControlModifier)) break;
475         case Qt::Key_Left:
476         case Qt::Key_Right:
477         case Qt::Key_Up:
478         case Qt::Key_Down:
479                 TextPanel::keyPressEvent(ev);
480                 break;
481         }
482 }
483
484 void TM::SourcePanel::do_click(QPoint const &pos)
485 {
486         if(m_editor->link_mode()) do_click_in_link_mode(pos);
487 }
488
489 void TM::SourcePanel::do_click_in_link_mode(QPoint const &pos)
490 {
491         Text::pointer w = select_word(pos);
492         if(!w) return;
493
494         m_linker.toggle(WordLink::Source, w);
495         ensure_highlight();
496         setTextCursor(cursorForPosition(pos));
497 }
498
499 void TM::SourcePanel::do_focusin()
500 {
501         //if(m_target_panel) m_target_panel->show();
502 }
503
504 void TM::SourcePanel::do_focusout()
505 {
506 }
507
508 // TargetPanel ----------------------------------------------------------------
509
510 TM::TargetPanel::TargetPanel(QWidget *parent)
511         : EditorPanel(parent)
512         , m_source_panel(nullptr)
513         , m_text_dirty(false)
514         , m_text_saved(true)
515 {
516         //setContextMenuPolicy(Qt::NoContextMenu);
517 }
518
519 TM::SourcePanel* TM::TargetPanel::source_panel() { return m_source_panel; }
520
521 void TM::TargetPanel::set_source_panel(SourcePanel *source)
522 {
523         m_source_panel = source;
524 }
525
526 void TM::TargetPanel::set_sentence(Text::pointer sentence)
527 {
528         EditorPanel::set_sentence(sentence);
529         set_text_dirty(false);
530         set_text_saved(false);
531 }
532
533 /*!
534  * \brief データベースへの登録が必要な場合、falseを返します。
535  *
536  * パネル内の文字列が編集された場合、trueを返します。
537  * 原文パネルがフォーカスを失うと、エディタがこのメンバを呼び出し、
538  * データベース登録の必要性を判定します。
539  * その後、エディタはデータベース登録を行い、フラグをクリアするので、
540  * 次に文字列が編集されるまでtrueを返し続けます。
541  */
542 bool TM::TargetPanel::is_text_saved() const { return m_text_saved; }
543
544 void TM::TargetPanel::set_text_saved(bool saved)
545 {
546         m_text_saved = saved;
547 }
548
549 /*!
550  * \brief パネル内の文字列が編集された場合、trueを返します。
551  *
552  * エディタがリンクモードに入るとき、このメンバを呼び出し、
553  * 単語へ分割する必要があるか判定します。
554  * エディタはパネルの文字列を単語に分割した後、set_sentence()によって
555  * パネルに文を設定するため、次に文字列を編集するまでfalseを返すようになります。
556  */
557 bool TM::TargetPanel::is_text_dirty() const { return m_text_dirty; }
558
559 /*!
560  * \brief パネルの文字列が編集されたかを示すフラグを設定します。
561  */
562 void TM::TargetPanel::set_text_dirty(bool dirty)
563 {
564         m_text_dirty = dirty;
565         if(dirty) m_editor->parent_editor_widget()->set_link_mode_disabled(false);
566 }
567
568 bool TM::TargetPanel::canInsertFromMimeData(QMimeData const *source) const
569 {
570         if(source->hasText()) return true;
571         return false;
572 }
573
574 void TM::TargetPanel::insertFromMimeData(QMimeData const *source)
575 {
576         TextPanel::insertFromMimeData(source);
577         set_text_dirty(true);
578         set_text_saved(false);
579 }
580
581 void TM::TargetPanel::inputMethodEvent(QInputMethodEvent *ev)
582 {
583         if(m_editor->link_mode()) ev->setCommitString("");
584         if(!ev->commitString().isEmpty())
585         {
586                 set_text_dirty(true);
587                 set_text_saved(false);
588         }
589         QPlainTextEdit::inputMethodEvent(ev);
590 }
591
592 void TM::TargetPanel::keyPressEvent(QKeyEvent *ev)
593 {
594         if(m_editor->link_mode()) do_key_press_in_link_mode(ev);
595         else
596         {
597                 TextPanel::keyPressEvent(ev);
598                 if(!ev->text().isEmpty())
599                 {
600                         set_text_dirty(true);
601                         set_text_saved(false);
602                 }
603         }
604 }
605
606 void TM::TargetPanel::do_click(QPoint const &pos)
607 {
608         if(m_editor->link_mode()) do_click_in_link_mode(pos);
609 }
610
611 void TM::TargetPanel::do_click_in_link_mode(QPoint const &pos)
612 {
613         Text::pointer w = select_word(pos);
614         if(!w) return;
615
616         WordLinker *wl = m_source_panel->linker();
617         wl->toggle(WordLink::Target, w);
618         m_source_panel->ensure_highlight();
619         setTextCursor(cursorForPosition(pos));
620 }
621
622 void TM::TargetPanel::do_focusin()
623 {
624         //show();
625 }
626
627 void TM::TargetPanel::do_focusout()
628 {
629         //hide();
630 }
631
632 void TM::TargetPanel::do_key_press_in_link_mode(QKeyEvent *ev)
633 {
634         int key = ev->key();
635         switch(key)
636         {
637         case Qt::Key_Enter:
638         case Qt::Key_Return:
639                 m_editor->set_link_mode(false);
640                 break;
641         case Qt::Key_Left:
642         case Qt::Key_Right:
643         case Qt::Key_Up:
644         case Qt::Key_Down:
645                 TextPanel::keyPressEvent(ev);
646                 break;
647         }
648 }
649
650 // Editor ---------------------------------------------------------------------
651
652 TM::Editor::Editor(Settings *settings, Service *service, QWidget *parent)
653         : TextWidget(parent)
654         , m_settings(settings)
655         , m_service(service)
656         , m_edit_mode(false)
657         , m_link_mode(false)
658         , m_current_source_panel(nullptr)
659 {
660         TextArea *ta = text_area();
661         connect(ta, SIGNAL(focusInChild(TextPanel*,TextPanel*)), this, SLOT(onFocusInChild(TextPanel*,TextPanel*)));
662 }
663
664 /*!
665  * \brief エディタが保持しているリソースを適切に解放します。
666  */
667 void TM::Editor::clear()
668 {
669         if(!m_sentences) return; // 文を保持していない場合、何もしない。
670
671         m_sentences.reset(); // 保持している文のメモリーを開放する。
672         // パネルの終了処理。
673         if(m_current_source_panel) do_panel_leave(m_current_source_panel);
674         m_current_source_panel = nullptr;
675         // 表示をクリアする。
676         TextArea *ta = text_area();
677         ta->clear();
678         // リンクモードの解除。
679         set_link_mode(false);
680         parent_editor_widget()->set_link_mode_disabled(true);
681 }
682
683 /*!
684  * \brief エディタに文を設定します。
685  *
686  * 文は複数の場合があります。
687  */
688 void TM::Editor::set_sentences(Text::pointer sentences)
689 {
690         clear();
691         m_sentences = sentences;
692         TextArea *ta = text_area();
693         for(Text::pointer s = sentences->begin(); s; s = s->next()) // s: sentence
694         {
695                 SourcePanel *sp = ta->append<SourcePanel>();
696                 TargetPanel *tp = ta->append<TargetPanel>();
697                 sp->set_target_panel(tp);
698                 sp->set_editor(this);
699                 tp->set_source_panel(sp);
700                 tp->set_editor(this);
701                 sp->set_sentence(s);
702                 sp->show();
703         }
704 }
705
706 bool TM::Editor::edit_mode() const { return m_edit_mode; }
707
708 void TM::Editor::set_edit_mode(bool mode_) { m_edit_mode = mode_; }
709
710 bool TM::Editor::link_mode() const { return m_link_mode; }
711
712 void TM::Editor::set_link_mode(bool mode_)
713 {
714         if(mode_ == m_link_mode) return; // モードが変化していない場合、何もしない。
715         assert(!mode_ || can_link_mode()); // リンクモードに入るには、条件がある。
716
717         m_link_mode = mode_;
718         parent_editor_widget()->set_link_mode(mode_);
719
720         if(mode_) do_link_mode_enter(m_current_source_panel);
721         else if(m_current_source_panel) do_link_mode_leave(m_current_source_panel);
722 }
723
724 bool TM::Editor::can_link_mode() const
725 {
726         if(!m_current_source_panel) return false;
727         if(!edit_mode()) return false;
728         if(current_target_panel()->is_empty()) return false;
729
730         return true;
731 }
732
733 TM::EditorWidget* TM::Editor::parent_editor_widget()
734 {
735         EditorWidget* result = qobject_cast<EditorWidget*>(parentWidget());
736         assert(result);
737         return result;
738 }
739
740 TM::TargetPanel* TM::Editor::current_target_panel()
741 {
742         TargetPanel *result = nullptr;
743         if(m_current_source_panel) result = m_current_source_panel->target_panel();
744         return result;
745 }
746
747 TM::TargetPanel const* TM::Editor::current_target_panel() const
748 {
749         return const_cast<Editor*>(this)->current_target_panel();
750 }
751
752 void TM::Editor::onFocusInChild(TextPanel *new_, TextPanel *)
753 {
754         SourcePanel *old_panel = m_current_source_panel;
755         SourcePanel *new_panel = find_source_panel(new_);
756         assert(new_panel);
757
758         if(old_panel && old_panel != new_panel) do_panel_leave(old_panel);
759         m_current_source_panel = new_panel;
760         if(old_panel != new_panel) do_panel_enter(new_panel);
761 }
762
763 TM::SourcePanel* TM::Editor::find_source_panel(TextPanel *panel)
764 {
765         SourcePanel *sp = qobject_cast<SourcePanel*>(panel);
766         if(sp) return sp;
767         TargetPanel *tp = qobject_cast<TargetPanel*>(panel);
768         if(tp) return tp->source_panel();
769         return nullptr;
770 }
771
772 /*!
773  * \brief 文を表示するパネルにフォーカスが入るとき呼び出されます。
774  */
775 void TM::Editor::do_panel_enter(SourcePanel *panel)
776 {
777         TargetPanel *tp = panel->target_panel();
778         assert(tp);
779         tp->show();
780         if(can_link_mode()) parent_editor_widget()->set_link_mode_disabled(false);
781 }
782
783 /*!
784  * \brief 文を表示するパネルからフォーカスが出るとき呼び出されます。
785  */
786 void TM::Editor::do_panel_leave(SourcePanel *panel)
787 {
788         TargetPanel *tp = panel->target_panel();
789         assert(tp);
790         tp->hide();
791
792         set_link_mode(false);
793         parent_editor_widget()->set_link_mode_disabled(true);
794
795         if(!tp->is_text_saved())
796         {
797                 //qDebug() << m_service->si
798                 tp->set_text_saved(true);
799         }
800 }
801
802 /*!
803  * \brief リンクモードに入るとき呼び出されます。
804  */
805 void TM::Editor::do_link_mode_enter(SourcePanel *panel)
806 {
807         assert(panel);
808         TargetPanel *tp = panel->target_panel();
809         assert(tp);
810         int tcode = parent_editor_widget()->target_language();
811
812         if(tp->is_text_dirty())
813         {
814                 panel->linker()->clear();
815                 QString string = tp->toPlainText();
816                 Text::pointer sentences = m_service->divide_into_sentences(tcode, string);
817                 if(sentences->size())
818                 {
819                         Text::pointer words = m_service->divide_into_words(tcode, sentences->begin());
820                         tp->set_sentence(words); // ココで、text_dirtyがfalseになる。
821                 }
822         }
823
824         panel->ensure_highlight();
825 }
826
827 /*!
828  * \brief リンクモードから出るとき呼び出されます。
829  */
830 void TM::Editor::do_link_mode_leave(SourcePanel *panel)
831 {
832         qDebug() << m_current_source_panel->linker()->to_json_array();
833         assert(panel);
834         panel->commit_link();
835         //panel->clear_highlight();
836 }
837
838
839
840
841
842
843
844
845
846