| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "gui/character_pad/hand_writing_canvas.h" |
| |
| #include <QtGui/QtGui> |
| #include "gui/character_pad/hand_writing.h" |
| |
| namespace mozc { |
| namespace gui { |
| |
| HandWritingCanvas::HandWritingCanvas(QWidget *parent) |
| : QWidget(parent), |
| list_widget_(NULL), is_drawing_(false), |
| handwriting_status_(handwriting::HANDWRITING_NO_ERROR) { |
| setBackgroundRole(QPalette::Base); |
| setAutoFillBackground(true); |
| strokes_.reserve(128); |
| QObject::connect(this, SIGNAL(startRecognition()), |
| &recognizer_thread_, SLOT(startRecognition()), |
| Qt::QueuedConnection); |
| QObject::connect(&recognizer_thread_, SIGNAL(candidatesUpdated()), |
| this, SLOT(listUpdated()), Qt::QueuedConnection); |
| qRegisterMetaType<mozc::handwriting::HandwritingStatus>( |
| "mozc::handwriting::HandwritingStatus"); |
| QObject::connect(&recognizer_thread_, |
| SIGNAL(statusUpdated(mozc::handwriting::HandwritingStatus)), |
| this, |
| SLOT(statusUpdated(mozc::handwriting::HandwritingStatus)), |
| Qt::QueuedConnection); |
| recognizer_thread_.Start(); |
| } |
| |
| HandWritingCanvas::~HandWritingCanvas() { |
| recognizer_thread_.quit(); |
| recognizer_thread_.wait(); |
| } |
| |
| void HandWritingCanvas::setListWidget(QListWidget *list_widget) { |
| list_widget_ = list_widget; |
| QObject::connect(list_widget_, SIGNAL(itemSelected(const QListWidgetItem*)), |
| &recognizer_thread_, |
| SLOT(itemSelected(const QListWidgetItem*)), |
| Qt::QueuedConnection); |
| } |
| |
| void HandWritingCanvas::clear() { |
| handwriting_status_ = handwriting::HANDWRITING_NO_ERROR; |
| strokes_.clear(); |
| update(); |
| is_drawing_ = false; |
| } |
| |
| void HandWritingCanvas::revert() { |
| handwriting_status_ = handwriting::HANDWRITING_NO_ERROR; |
| if (!strokes_.empty()) { |
| strokes_.resize(strokes_.size() - 1); |
| update(); |
| recognize(); |
| } |
| is_drawing_ = false; |
| } |
| |
| void HandWritingCanvas::restartRecognition() { |
| // We need to call |recognize()| instead of |emit startRecognition()| here |
| // so that the current stroke has a new timestamp. |
| recognize(); |
| } |
| |
| void HandWritingCanvas::paintEvent(QPaintEvent *) { |
| QPainter painter(this); |
| |
| // show grid information |
| painter.setPen(QPen(Qt::gray, 1)); |
| |
| const QRect border_rect(0, 0, width() - 1, height() - 1); |
| painter.drawRect(border_rect); |
| |
| const int diff = static_cast<int>(height() * 0.05); |
| const int margin = static_cast<int>(height() * 0.04); |
| painter.drawLine(width() / 2 - diff, height() / 2, |
| width() / 2 + diff, height() / 2); |
| painter.drawLine(width() / 2, height() / 2 - diff, |
| width() / 2, height() / 2 + diff); |
| |
| painter.drawLine(margin, margin, margin + diff, margin); |
| painter.drawLine(margin, margin, margin, margin + diff); |
| |
| painter.drawLine(width() - margin - diff, margin, |
| width() - margin, margin); |
| painter.drawLine(width() - margin, margin, |
| width() - margin, margin + diff); |
| |
| painter.drawLine(margin, height() - margin - diff, |
| margin, height() - margin); |
| painter.drawLine(margin, height() - margin, |
| margin + diff, height() - margin); |
| |
| painter.drawLine(width() - margin - diff, height() - margin, |
| width() - margin, height() - margin); |
| painter.drawLine(width() - margin, height() - margin - diff, |
| width() - margin, height() - margin); |
| |
| if (strokes_.empty()) { |
| painter.drawText(margin + 10, margin + 10, |
| width() - margin - 20, height() / 2, |
| Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, |
| QObject::tr("Draw a character here")); |
| } |
| |
| // show pen strokes |
| painter.setPen(QPen(Qt::black, 3)); |
| |
| for (int i = 0; i < strokes_.size(); ++i) { |
| for (int j = 1; j < strokes_[i].size(); ++j) { |
| const int x1 = static_cast<int>(strokes_[i][j - 1].first * width()); |
| const int y1 = static_cast<int>(strokes_[i][j - 1].second * height()); |
| const int x2 = static_cast<int>(strokes_[i][j].first * width()); |
| const int y2 = static_cast<int>(strokes_[i][j].second * height()); |
| painter.drawLine(x1, y1, x2, y2); |
| } |
| } |
| |
| if (handwriting_status_ != handwriting::HANDWRITING_NO_ERROR) { |
| painter.setPen(QPen(Qt::red, 2)); |
| QString warning_message; |
| switch (handwriting_status_) { |
| case handwriting::HANDWRITING_ERROR: |
| warning_message = QObject::tr("error"); |
| break; |
| case handwriting::HANDWRITING_NETWORK_ERROR: |
| warning_message = QObject::tr("network error"); |
| break; |
| case handwriting::HANDWRITING_UNKNOWN_ERROR: |
| default: |
| warning_message = QObject::tr("unknown error"); |
| break; |
| } |
| painter.drawText(0, 0, width() - margin, height() - margin, |
| Qt::AlignRight | Qt::AlignBottom | Qt::TextWordWrap, |
| warning_message); |
| } |
| |
| emit canvasUpdated(); |
| } |
| |
| void HandWritingCanvas::recognize() { |
| if (strokes_.empty()) { |
| return; |
| } |
| |
| recognizer_thread_.SetStrokes(strokes_); |
| emit startRecognition(); |
| } |
| |
| void HandWritingCanvas::listUpdated() { |
| vector<string> candidates; |
| recognizer_thread_.GetCandidates(&candidates); |
| |
| list_widget_->clear(); |
| for (size_t i = 0; i < candidates.size(); ++i) { |
| list_widget_->addItem(QString::fromUtf8(candidates[i].c_str())); |
| } |
| } |
| |
| void HandWritingCanvas::statusUpdated(handwriting::HandwritingStatus status) { |
| handwriting_status_ = status; |
| update(); |
| } |
| |
| void HandWritingCanvas::mousePressEvent(QMouseEvent *event) { |
| if (event->button() != Qt::LeftButton) { |
| return; |
| } |
| |
| strokes_.resize(strokes_.size() + 1); |
| const float x = static_cast<float>(event->pos().x()) / width(); |
| const float y = static_cast<float>(event->pos().y()) / height(); |
| strokes_.back().push_back(make_pair(x, y)); |
| is_drawing_ = true; |
| update(); |
| } |
| |
| void HandWritingCanvas::mouseMoveEvent(QMouseEvent *event) { |
| if (!is_drawing_) { |
| return; |
| } |
| |
| const float x = static_cast<float>(event->pos().x()) / width(); |
| const float y = static_cast<float>(event->pos().y()) / height(); |
| strokes_.back().push_back(make_pair(x, y)); |
| update(); |
| } |
| |
| void HandWritingCanvas::mouseReleaseEvent(QMouseEvent *event) { |
| if (event->button() != Qt::LeftButton) { |
| return; |
| } |
| |
| is_drawing_ = false; |
| update(); |
| recognize(); |
| } |
| |
| size_t HandWritingCanvas::strokes_size() const { |
| return strokes_.size(); |
| } |
| } // namespace gui |
| } // namespace mozc |