OSDN Git Service

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