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