1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "gui/character_pad/hand_writing_canvas.h"
31 
32 #include <QtGui/QtGui>
33 #include "gui/character_pad/hand_writing.h"
34 
35 namespace mozc {
36 namespace gui {
37 
HandWritingCanvas(QWidget * parent)38 HandWritingCanvas::HandWritingCanvas(QWidget *parent)
39     : QWidget(parent),
40       list_widget_(NULL), is_drawing_(false),
41       handwriting_status_(handwriting::HANDWRITING_NO_ERROR) {
42   setBackgroundRole(QPalette::Base);
43   setAutoFillBackground(true);
44   strokes_.reserve(128);
45   QObject::connect(this, SIGNAL(startRecognition()),
46                    &recognizer_thread_, SLOT(startRecognition()),
47                    Qt::QueuedConnection);
48   QObject::connect(&recognizer_thread_, SIGNAL(candidatesUpdated()),
49                    this, SLOT(listUpdated()), Qt::QueuedConnection);
50   qRegisterMetaType<mozc::handwriting::HandwritingStatus>(
51       "mozc::handwriting::HandwritingStatus");
52   QObject::connect(&recognizer_thread_,
53                    SIGNAL(statusUpdated(mozc::handwriting::HandwritingStatus)),
54                    this,
55                    SLOT(statusUpdated(mozc::handwriting::HandwritingStatus)),
56                    Qt::QueuedConnection);
57   recognizer_thread_.Start();
58 }
59 
~HandWritingCanvas()60 HandWritingCanvas::~HandWritingCanvas() {
61   recognizer_thread_.quit();
62   recognizer_thread_.wait();
63 }
64 
setListWidget(QListWidget * list_widget)65 void HandWritingCanvas::setListWidget(QListWidget *list_widget)  {
66   list_widget_ = list_widget;
67   QObject::connect(list_widget_, SIGNAL(itemSelected(const QListWidgetItem*)),
68                    &recognizer_thread_,
69                    SLOT(itemSelected(const QListWidgetItem*)),
70                    Qt::QueuedConnection);
71 }
72 
clear()73 void HandWritingCanvas::clear() {
74   handwriting_status_ = handwriting::HANDWRITING_NO_ERROR;
75   strokes_.clear();
76   update();
77   is_drawing_ = false;
78 }
79 
revert()80 void HandWritingCanvas::revert() {
81   handwriting_status_ = handwriting::HANDWRITING_NO_ERROR;
82   if (!strokes_.empty()) {
83     strokes_.resize(strokes_.size() - 1);
84     update();
85     recognize();
86   }
87   is_drawing_ = false;
88 }
89 
restartRecognition()90 void HandWritingCanvas::restartRecognition() {
91   // We need to call |recognize()| instead of |emit startRecognition()| here
92   // so that the current stroke has a new timestamp.
93   recognize();
94 }
95 
paintEvent(QPaintEvent *)96 void HandWritingCanvas::paintEvent(QPaintEvent *) {
97   QPainter painter(this);
98 
99   // show grid information
100   painter.setPen(QPen(Qt::gray, 1));
101 
102   const QRect border_rect(0, 0, width() - 1, height() - 1);
103   painter.drawRect(border_rect);
104 
105   const int diff = static_cast<int>(height() * 0.05);
106   const int margin = static_cast<int>(height() * 0.04);
107   painter.drawLine(width() / 2 - diff, height() / 2,
108                    width() / 2 + diff, height() / 2);
109   painter.drawLine(width() / 2, height() / 2  - diff,
110                    width() / 2, height() / 2 + diff);
111 
112   painter.drawLine(margin, margin, margin + diff, margin);
113   painter.drawLine(margin, margin, margin, margin + diff);
114 
115   painter.drawLine(width() - margin - diff, margin,
116                    width() - margin, margin);
117   painter.drawLine(width() - margin, margin,
118                    width() - margin, margin + diff);
119 
120   painter.drawLine(margin, height() - margin - diff,
121                    margin, height() - margin);
122   painter.drawLine(margin, height() - margin,
123                    margin + diff, height() - margin);
124 
125   painter.drawLine(width() - margin - diff, height() - margin,
126                    width() - margin, height() - margin);
127   painter.drawLine(width() - margin, height() - margin - diff,
128                    width() - margin, height() - margin);
129 
130   if (strokes_.empty()) {
131     painter.drawText(margin + 10, margin + 10,
132                      width() - margin - 20,  height() / 2,
133                      Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
134                      QObject::tr("Draw a character here"));
135   }
136 
137   // show pen strokes
138   painter.setPen(QPen(Qt::black, 3));
139 
140   for (int i = 0; i < strokes_.size(); ++i) {
141     for (int j = 1; j < strokes_[i].size(); ++j) {
142       const int x1 = static_cast<int>(strokes_[i][j - 1].first * width());
143       const int y1 = static_cast<int>(strokes_[i][j - 1].second * height());
144       const int x2 = static_cast<int>(strokes_[i][j].first * width());
145       const int y2 = static_cast<int>(strokes_[i][j].second * height());
146       painter.drawLine(x1, y1, x2, y2);
147     }
148   }
149 
150   if (handwriting_status_ != handwriting::HANDWRITING_NO_ERROR) {
151     painter.setPen(QPen(Qt::red, 2));
152     QString warning_message;
153     switch (handwriting_status_) {
154       case handwriting::HANDWRITING_ERROR:
155         warning_message = QObject::tr("error");
156         break;
157       case handwriting::HANDWRITING_NETWORK_ERROR:
158         warning_message = QObject::tr("network error");
159         break;
160       case handwriting::HANDWRITING_UNKNOWN_ERROR:
161       default:
162         warning_message = QObject::tr("unknown error");
163         break;
164     }
165     painter.drawText(0, 0, width() - margin, height() - margin,
166                      Qt::AlignRight | Qt::AlignBottom | Qt::TextWordWrap,
167                      warning_message);
168   }
169 
170   emit canvasUpdated();
171 }
172 
recognize()173 void HandWritingCanvas::recognize() {
174   if (strokes_.empty()) {
175     return;
176   }
177 
178   recognizer_thread_.SetStrokes(strokes_);
179   emit startRecognition();
180 }
181 
listUpdated()182 void HandWritingCanvas::listUpdated() {
183   std::vector<string> candidates;
184   recognizer_thread_.GetCandidates(&candidates);
185 
186   list_widget_->clear();
187   for (size_t i = 0; i < candidates.size(); ++i) {
188     list_widget_->addItem(QString::fromUtf8(candidates[i].c_str()));
189   }
190 }
191 
statusUpdated(handwriting::HandwritingStatus status)192 void HandWritingCanvas::statusUpdated(handwriting::HandwritingStatus status) {
193   handwriting_status_ = status;
194   update();
195 }
196 
mousePressEvent(QMouseEvent * event)197 void HandWritingCanvas::mousePressEvent(QMouseEvent *event) {
198   if (event->button() != Qt::LeftButton) {
199     return;
200   }
201 
202   strokes_.resize(strokes_.size() + 1);
203   const float x = static_cast<float>(event->pos().x()) / width();
204   const float y = static_cast<float>(event->pos().y()) / height();
205   strokes_.back().push_back(std::make_pair(x, y));
206   is_drawing_ = true;
207   update();
208 }
209 
mouseMoveEvent(QMouseEvent * event)210 void HandWritingCanvas::mouseMoveEvent(QMouseEvent *event) {
211   if (!is_drawing_) {
212     return;
213   }
214 
215   const float x = static_cast<float>(event->pos().x()) / width();
216   const float y = static_cast<float>(event->pos().y()) / height();
217   strokes_.back().push_back(std::make_pair(x, y));
218   update();
219 }
220 
mouseReleaseEvent(QMouseEvent * event)221 void HandWritingCanvas::mouseReleaseEvent(QMouseEvent *event) {
222   if (event->button() != Qt::LeftButton) {
223     return;
224   }
225 
226   is_drawing_ = false;
227   update();
228   recognize();
229 }
230 
strokes_size() const231 size_t HandWritingCanvas::strokes_size() const {
232   return strokes_.size();
233 }
234 }  // namespace gui
235 }  // namespace mozc
236