Home | History | Annotate | Download | only in ime
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 #include <string>
      7 #include <utility>
      8 #include <vector>
      9 
     10 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
     11 #include "ppapi/c/ppb_console.h"
     12 #include "ppapi/cpp/completion_callback.h"
     13 #include "ppapi/cpp/dev/font_dev.h"
     14 #include "ppapi/cpp/graphics_2d.h"
     15 #include "ppapi/cpp/image_data.h"
     16 #include "ppapi/cpp/input_event.h"
     17 #include "ppapi/cpp/instance.h"
     18 #include "ppapi/cpp/module.h"
     19 #include "ppapi/cpp/rect.h"
     20 #include "ppapi/cpp/size.h"
     21 #include "ppapi/cpp/text_input_controller.h"
     22 
     23 namespace {
     24 
     25 // Extracted from: ui/events/keycodes/keyboard_codes.h
     26 enum {
     27   VKEY_BACK = 0x08,
     28   VKEY_SHIFT = 0x10,
     29   VKEY_DELETE = 0x2E,
     30   VKEY_LEFT = 0x25,
     31   VKEY_UP = 0x26,
     32   VKEY_RIGHT = 0x27,
     33   VKEY_DOWN = 0x28,
     34 };
     35 
     36 const uint32_t kTextfieldBgColor = 0xffffffff;
     37 const uint32_t kTextfieldTextColor = 0xff000000;
     38 const uint32_t kTextfieldCaretColor = 0xff000000;
     39 const uint32_t kTextfieldPreeditTextColor = 0xffff0000;
     40 const uint32_t kTextfieldSelectionBackgroundColor = 0xffeecccc;
     41 const uint32_t kTextfieldUnderlineColorMain = 0xffff0000;
     42 const uint32_t kTextfieldUnderlineColorSub = 0xffddaaaa;
     43 
     44 void FillRect(pp::ImageData* image,
     45               int left, int top, int width, int height,
     46               uint32_t color) {
     47   for (int y = std::max(0, top);
     48        y < std::min(image->size().height() - 1, top + height);
     49        ++y) {
     50     for (int x = std::max(0, left);
     51          x < std::min(image->size().width() - 1, left + width);
     52          ++x)
     53       *image->GetAddr32(pp::Point(x, y)) = color;
     54   }
     55 }
     56 
     57 void FillRect(pp::ImageData* image, const pp::Rect& rect, uint32_t color) {
     58   FillRect(image, rect.x(), rect.y(), rect.width(), rect.height(), color);
     59 }
     60 
     61 size_t GetPrevCharOffsetUtf8(const std::string& str, size_t current_pos) {
     62   size_t i = current_pos;
     63   if (i > 0) {
     64     do
     65       --i;
     66     while (i > 0 && (str[i] & 0xc0) == 0x80);
     67   }
     68   return i;
     69 }
     70 
     71 size_t GetNextCharOffsetUtf8(const std::string& str, size_t current_pos) {
     72   size_t i = current_pos;
     73   if (i < str.size()) {
     74     do
     75       ++i;
     76     while (i < str.size() && (str[i] & 0xc0) == 0x80);
     77   }
     78   return i;
     79 }
     80 
     81 size_t GetNthCharOffsetUtf8(const std::string& str, size_t n) {
     82   size_t i = 0;
     83   for (size_t step = 0; step < n; ++step)
     84     i = GetNextCharOffsetUtf8(str, i);
     85   return i;
     86 }
     87 
     88 }  // namespace
     89 
     90 class TextFieldStatusHandler {
     91  public:
     92   virtual ~TextFieldStatusHandler() {}
     93   virtual void FocusIn(const pp::Rect& caret) {}
     94   virtual void FocusOut() {}
     95   virtual void UpdateSelection(const std::string& text) {}
     96 };
     97 
     98 class TextFieldStatusNotifyingHandler : public TextFieldStatusHandler {
     99  public:
    100   explicit TextFieldStatusNotifyingHandler(pp::Instance* instance)
    101       : textinput_control_(instance) {
    102   }
    103 
    104  protected:
    105   // Implement TextFieldStatusHandler.
    106   virtual void FocusIn(const pp::Rect& caret) {
    107     textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_TEXT);
    108     textinput_control_.UpdateCaretPosition(caret);
    109   }
    110   virtual void FocusOut() {
    111     textinput_control_.CancelCompositionText();
    112     textinput_control_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
    113   }
    114   virtual void UpdateSelection(const std::string& text) {
    115     textinput_control_.UpdateSurroundingText(text, 0, text.size());
    116   }
    117 
    118  private:
    119   pp::TextInputController textinput_control_;
    120 };
    121 
    122 // Hand-made text field for demonstrating text input API.
    123 class MyTextField {
    124  public:
    125   MyTextField(pp::Instance* instance, TextFieldStatusHandler* handler,
    126               int x, int y, int width, int height)
    127       : instance_(instance),
    128         status_handler_(handler),
    129         area_(x, y, width, height),
    130         font_size_(height - 2),
    131         caret_pos_(std::string::npos),
    132         anchor_pos_(std::string::npos),
    133         target_segment_(0) {
    134     pp::FontDescription_Dev desc;
    135     desc.set_family(PP_FONTFAMILY_SANSSERIF);
    136     desc.set_size(font_size_);
    137     font_ = pp::Font_Dev(instance_, desc);
    138   }
    139 
    140   // Paint on the specified ImageData.
    141   void PaintOn(pp::ImageData* image, pp::Rect clip) {
    142     clip = clip.Intersect(area_);
    143     FillRect(image, clip, kTextfieldBgColor);
    144 
    145     if (caret_pos_ != std::string::npos) {
    146       int offset = area_.x();
    147       // selection (for the case without composition text)
    148       if (composition_.empty() && HasSelection()) {
    149         int left_x = font_.MeasureSimpleText(
    150             utf8_text_.substr(0, SelectionLeft()));
    151         int right_x = font_.MeasureSimpleText(
    152             utf8_text_.substr(0, SelectionRight()));
    153         FillRect(image, offset + left_x, area_.y(), right_x - left_x,
    154                  area_.height(), kTextfieldSelectionBackgroundColor);
    155       }
    156       // before caret
    157       {
    158         std::string str = utf8_text_.substr(0, caret_pos_);
    159         font_.DrawTextAt(
    160             image,
    161             pp::TextRun_Dev(str.c_str(), false, false),
    162             pp::Point(offset, area_.y() + font_size_),
    163             kTextfieldTextColor,
    164             clip,
    165             false);
    166         offset += font_.MeasureSimpleText(str);
    167       }
    168       // composition
    169       {
    170         const std::string& str = composition_;
    171         // selection
    172         if (composition_selection_.first != composition_selection_.second) {
    173           int left_x = font_.MeasureSimpleText(
    174               str.substr(0, composition_selection_.first));
    175           int right_x = font_.MeasureSimpleText(
    176               str.substr(0, composition_selection_.second));
    177           FillRect(image, offset + left_x, area_.y(), right_x - left_x,
    178                    area_.height(), kTextfieldSelectionBackgroundColor);
    179         }
    180         // composition text
    181         font_.DrawTextAt(
    182             image,
    183             pp::TextRun_Dev(str.c_str(), false, false),
    184             pp::Point(offset, area_.y() + font_size_),
    185             kTextfieldPreeditTextColor,
    186             clip,
    187             false);
    188         for (size_t i = 0; i < segments_.size(); ++i) {
    189           size_t l = segments_[i].first;
    190           size_t r = segments_[i].second;
    191           if (l != r) {
    192             int lx = font_.MeasureSimpleText(str.substr(0, l));
    193             int rx = font_.MeasureSimpleText(str.substr(0, r));
    194             FillRect(image,
    195                      offset + lx + 2, area_.y() + font_size_ + 1,
    196                      rx - lx - 4, 2,
    197                      i == static_cast<size_t>(target_segment_) ?
    198                          kTextfieldUnderlineColorMain :
    199                          kTextfieldUnderlineColorSub);
    200           }
    201         }
    202         // caret
    203         int caretx = font_.MeasureSimpleText(
    204             str.substr(0, composition_selection_.first));
    205         FillRect(image,
    206                  pp::Rect(offset + caretx, area_.y(), 2, area_.height()),
    207                  kTextfieldCaretColor);
    208         offset += font_.MeasureSimpleText(str);
    209       }
    210       // after caret
    211       {
    212         std::string str = utf8_text_.substr(caret_pos_);
    213         font_.DrawTextAt(
    214             image,
    215             pp::TextRun_Dev(str.c_str(), false, false),
    216             pp::Point(offset, area_.y() + font_size_),
    217             kTextfieldTextColor,
    218             clip,
    219             false);
    220       }
    221     } else {
    222       font_.DrawTextAt(
    223           image,
    224           pp::TextRun_Dev(utf8_text_.c_str(), false, false),
    225           pp::Point(area_.x(), area_.y() + font_size_),
    226           kTextfieldTextColor,
    227           clip,
    228           false);
    229     }
    230   }
    231 
    232   // Update current composition text.
    233   void SetComposition(
    234       const std::string& text,
    235       const std::vector< std::pair<uint32_t, uint32_t> >& segments,
    236       int32_t target_segment,
    237       const std::pair<uint32_t, uint32_t>& selection) {
    238     if (HasSelection() && !text.empty())
    239       InsertText(std::string());
    240     composition_ = text;
    241     segments_ = segments;
    242     target_segment_ = target_segment;
    243     composition_selection_ = selection;
    244     CaretPosChanged();
    245   }
    246 
    247   // Is the text field focused?
    248   bool Focused() const {
    249     return caret_pos_ != std::string::npos;
    250   }
    251 
    252   // Does the coordinate (x,y) is contained inside the edit box?
    253   bool Contains(int x, int y) const {
    254     return area_.Contains(x, y);
    255   }
    256 
    257   // Resets the content text.
    258   void SetText(const std::string& text) {
    259     utf8_text_ = text;
    260     if (Focused()) {
    261       caret_pos_ = anchor_pos_ = text.size();
    262       CaretPosChanged();
    263     }
    264   }
    265 
    266   // Inserts a text at the current caret position.
    267   void InsertText(const std::string& text) {
    268     if (!Focused())
    269       return;
    270     utf8_text_.replace(SelectionLeft(), SelectionRight() - SelectionLeft(),
    271                        text);
    272     caret_pos_ = anchor_pos_ = SelectionLeft() + text.size();
    273     CaretPosChanged();
    274   }
    275 
    276   // Handles mouse click event and changes the focus state.
    277   bool RefocusByMouseClick(int x, int y) {
    278     if (!Contains(x, y)) {
    279       // The text field is unfocused.
    280       caret_pos_ = anchor_pos_ = std::string::npos;
    281       return false;
    282     }
    283 
    284     // The text field is focused.
    285     size_t n = font_.CharacterOffsetForPixel(
    286         pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
    287     caret_pos_ = anchor_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
    288     CaretPosChanged();
    289     return true;
    290   }
    291 
    292   void MouseDrag(int x, int y) {
    293     if (!Focused())
    294       return;
    295     size_t n = font_.CharacterOffsetForPixel(
    296         pp::TextRun_Dev(utf8_text_.c_str()), x - area_.x());
    297     caret_pos_ = GetNthCharOffsetUtf8(utf8_text_, n);
    298   }
    299 
    300   void MouseUp(int x, int y) {
    301     if (!Focused())
    302       return;
    303     CaretPosChanged();
    304   }
    305 
    306   void KeyLeft(bool shift) {
    307     if (!Focused())
    308       return;
    309     // Move caret to the head of the selection or to the previous character.
    310     if (!shift && HasSelection())
    311       caret_pos_ = SelectionLeft();
    312     else
    313       caret_pos_ = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
    314     // Move the anchor if the shift key is not pressed.
    315     if (!shift)
    316       anchor_pos_ = caret_pos_;
    317     CaretPosChanged();
    318   }
    319 
    320   void KeyRight(bool shift) {
    321     if (!Focused())
    322       return;
    323     // Move caret to the end of the selection or to the next character.
    324     if (!shift && HasSelection())
    325       caret_pos_ = SelectionRight();
    326     else
    327       caret_pos_ = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
    328     // Move the anchor if the shift key is not pressed.
    329     if (!shift)
    330       anchor_pos_ = caret_pos_;
    331     CaretPosChanged();
    332   }
    333 
    334   void KeyDelete() {
    335     if (!Focused())
    336       return;
    337     if (HasSelection()) {
    338       InsertText(std::string());
    339     } else {
    340       size_t i = GetNextCharOffsetUtf8(utf8_text_, caret_pos_);
    341       utf8_text_.erase(caret_pos_, i - caret_pos_);
    342       CaretPosChanged();
    343     }
    344   }
    345 
    346   void KeyBackspace() {
    347     if (!Focused())
    348       return;
    349     if (HasSelection()) {
    350       InsertText(std::string());
    351     } else if (caret_pos_ != 0) {
    352       size_t i = GetPrevCharOffsetUtf8(utf8_text_, caret_pos_);
    353       utf8_text_.erase(i, caret_pos_ - i);
    354       caret_pos_ = anchor_pos_ = i;
    355       CaretPosChanged();
    356     }
    357   }
    358 
    359  private:
    360   // Notify the plugin instance that the caret position has changed.
    361   void CaretPosChanged() {
    362     if (Focused()) {
    363       std::string str = utf8_text_.substr(0, caret_pos_);
    364       if (!composition_.empty())
    365         str += composition_.substr(0, composition_selection_.first);
    366       int px = font_.MeasureSimpleText(str);
    367       pp::Rect caret(area_.x() + px, area_.y(), 0, area_.height() + 2);
    368       status_handler_->FocusIn(caret);
    369       status_handler_->UpdateSelection(
    370           utf8_text_.substr(SelectionLeft(),
    371                             SelectionRight() - SelectionLeft()));
    372     }
    373   }
    374   size_t SelectionLeft() const {
    375     return std::min(caret_pos_, anchor_pos_);
    376   }
    377   size_t SelectionRight() const {
    378     return std::max(caret_pos_, anchor_pos_);
    379   }
    380   bool HasSelection() const {
    381     return caret_pos_ != anchor_pos_;
    382   }
    383 
    384   pp::Instance* instance_;
    385   TextFieldStatusHandler* status_handler_;
    386 
    387   pp::Rect area_;
    388   int font_size_;
    389   pp::Font_Dev font_;
    390   std::string utf8_text_;
    391   size_t caret_pos_;
    392   size_t anchor_pos_;
    393   std::string composition_;
    394   std::vector< std::pair<uint32_t, uint32_t> > segments_;
    395   std::pair<uint32_t, uint32_t> composition_selection_;
    396   int target_segment_;
    397 };
    398 
    399 class MyInstance : public pp::Instance {
    400  public:
    401   explicit MyInstance(PP_Instance instance)
    402       : pp::Instance(instance),
    403         status_handler_(new TextFieldStatusHandler),
    404         dragging_(false) {
    405   }
    406 
    407   ~MyInstance() {
    408     delete status_handler_;
    409   }
    410 
    411   virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
    412     RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
    413     RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
    414 
    415     for (uint32_t i = 0; i < argc; ++i) {
    416       if (argn[i] == std::string("ime")) {
    417         if (argv[i] == std::string("no")) {
    418           // Example of NO-IME plugins (e.g., games).
    419           //
    420           // When a plugin never wants to accept text input, at initialization
    421           // explicitly turn off the text input feature by calling:
    422           pp::TextInputController(this).SetTextInputType(
    423               PP_TEXTINPUT_TYPE_NONE);
    424         } else if (argv[i] == std::string("unaware")) {
    425           // Demonstrating the behavior of IME-unaware plugins.
    426           // Never call any text input related APIs.
    427           //
    428           // In such a case, the plugin is assumed to always accept text input.
    429           // For example, when the plugin is focused in touch devices a virtual
    430           // keyboard may pop up, or in environment IME is used, users can type
    431           // text via IME on the plugin. The characters are delivered to the
    432           // plugin via PP_INPUTEVENT_TYPE_CHAR events.
    433         } else if (argv[i] == std::string("caretmove")) {
    434           // Demonstrating the behavior of plugins with limited IME support.
    435           //
    436           // It uses SetTextInputType() and UpdateCaretPosition() API to notify
    437           // text input status to the browser, but unable to handle inline
    438           // compositions. By using the notified information. the browser can,
    439           // say, show virtual keyboards or IMEs only at appropriate timing
    440           // that the plugin does need to accept text input.
    441           delete status_handler_;
    442           status_handler_ = new TextFieldStatusNotifyingHandler(this);
    443         } else if (argv[i] == std::string("full")) {
    444           // Demonstrating the behavior of plugins fully supporting IME.
    445           //
    446           // It notifies updates of caret positions to the browser,
    447           // and handles all text input events by itself.
    448           delete status_handler_;
    449           status_handler_ = new TextFieldStatusNotifyingHandler(this);
    450           RequestInputEvents(PP_INPUTEVENT_CLASS_IME);
    451         }
    452         break;
    453       }
    454     }
    455 
    456     textfield_.push_back(MyTextField(this, status_handler_,
    457                                      10, 10, 300, 20));
    458     textfield_.back().SetText("Hello");
    459     textfield_.push_back(MyTextField(this, status_handler_,
    460                                      30, 100, 300, 20));
    461     textfield_.back().SetText("World");
    462     return true;
    463   }
    464 
    465  protected:
    466   virtual bool HandleInputEvent(const pp::InputEvent& event) {
    467     bool ret = false;
    468     switch (event.GetType()) {
    469       case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
    470         const pp::MouseInputEvent mouseEvent(event);
    471         ret = OnMouseDown(mouseEvent);
    472         break;
    473       }
    474       case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
    475         const pp::MouseInputEvent mouseEvent(event);
    476         ret = OnMouseMove(mouseEvent);
    477         break;
    478       }
    479       case PP_INPUTEVENT_TYPE_MOUSEUP: {
    480         const pp::MouseInputEvent mouseEvent(event);
    481         ret = OnMouseUp(mouseEvent);
    482         break;
    483       }
    484       case PP_INPUTEVENT_TYPE_KEYDOWN: {
    485         Log("Keydown");
    486         const pp::KeyboardInputEvent keyEvent(event);
    487         ret = OnKeyDown(keyEvent);
    488         break;
    489       }
    490       case PP_INPUTEVENT_TYPE_CHAR: {
    491         const pp::KeyboardInputEvent keyEvent(event);
    492         Log("Char [" + keyEvent.GetCharacterText().AsString() + "]");
    493         ret = OnChar(keyEvent);
    494         break;
    495       }
    496       case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: {
    497         const pp::IMEInputEvent imeEvent(event);
    498         Log("CompositionStart [" + imeEvent.GetText().AsString() + "]");
    499         ret = true;
    500         break;
    501       }
    502       case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: {
    503         const pp::IMEInputEvent imeEvent(event);
    504         Log("CompositionUpdate [" + imeEvent.GetText().AsString() + "]");
    505         ret = OnCompositionUpdate(imeEvent);
    506         break;
    507       }
    508       case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: {
    509         const pp::IMEInputEvent imeEvent(event);
    510         Log("CompositionEnd [" + imeEvent.GetText().AsString() + "]");
    511         ret = OnCompositionEnd(imeEvent);
    512         break;
    513       }
    514       case PP_INPUTEVENT_TYPE_IME_TEXT: {
    515         const pp::IMEInputEvent imeEvent(event);
    516         Log("ImeText [" + imeEvent.GetText().AsString() + "]");
    517         ret = OnImeText(imeEvent);
    518         break;
    519       }
    520       default:
    521         break;
    522     }
    523     if (ret && (dragging_ || event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE))
    524       Paint();
    525     return ret;
    526   }
    527 
    528   virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
    529     if (position.size() == last_size_)
    530       return;
    531     last_size_ = position.size();
    532     Paint();
    533   }
    534 
    535  private:
    536   bool OnCompositionUpdate(const pp::IMEInputEvent& ev) {
    537     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    538          it != textfield_.end();
    539          ++it) {
    540       if (it->Focused()) {
    541         std::vector< std::pair<uint32_t, uint32_t> > segs;
    542         for (uint32_t i = 0; i < ev.GetSegmentNumber(); ++i)
    543           segs.push_back(std::make_pair(ev.GetSegmentOffset(i),
    544                                         ev.GetSegmentOffset(i + 1)));
    545         uint32_t selection_start;
    546         uint32_t selection_end;
    547         ev.GetSelection(&selection_start, &selection_end);
    548         it->SetComposition(ev.GetText().AsString(),
    549                            segs,
    550                            ev.GetTargetSegment(),
    551                            std::make_pair(selection_start, selection_end));
    552         return true;
    553       }
    554     }
    555     return false;
    556   }
    557 
    558   bool OnCompositionEnd(const pp::IMEInputEvent& ev) {
    559     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    560          it != textfield_.end();
    561          ++it) {
    562       if (it->Focused()) {
    563         it->SetComposition(std::string(),
    564                            std::vector<std::pair<uint32_t, uint32_t> >(),
    565                            0,
    566                            std::make_pair(0, 0));
    567         return true;
    568       }
    569     }
    570     return false;
    571   }
    572 
    573   bool OnMouseDown(const pp::MouseInputEvent& ev) {
    574     dragging_ = true;
    575 
    576     bool anyone_focused = false;
    577     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    578          it != textfield_.end();
    579          ++it) {
    580       if (it->RefocusByMouseClick(ev.GetPosition().x(),
    581                                   ev.GetPosition().y())) {
    582         anyone_focused = true;
    583       }
    584     }
    585     if (!anyone_focused)
    586       status_handler_->FocusOut();
    587     return true;
    588   }
    589 
    590   bool OnMouseMove(const pp::MouseInputEvent& ev) {
    591     const PPB_CursorControl_Dev* cursor_control =
    592         reinterpret_cast<const PPB_CursorControl_Dev*>(
    593             pp::Module::Get()->GetBrowserInterface(
    594                 PPB_CURSOR_CONTROL_DEV_INTERFACE));
    595     if (!cursor_control)
    596       return false;
    597 
    598     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    599          it != textfield_.end();
    600          ++it) {
    601       if (it->Contains(ev.GetPosition().x(),
    602                        ev.GetPosition().y())) {
    603         cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_IBEAM,
    604                                   0, NULL);
    605         if (it->Focused() && dragging_)
    606           it->MouseDrag(ev.GetPosition().x(), ev.GetPosition().y());
    607         return true;
    608       }
    609     }
    610     cursor_control->SetCursor(pp_instance(), PP_CURSORTYPE_POINTER,
    611                               0, NULL);
    612     return true;
    613   }
    614 
    615   bool OnMouseUp(const pp::MouseInputEvent& ev) {
    616     dragging_ = false;
    617     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    618          it != textfield_.end();
    619          ++it)
    620       if (it->Focused())
    621         it->MouseUp(ev.GetPosition().x(), ev.GetPosition().y());
    622     return false;
    623   }
    624 
    625   bool OnKeyDown(const pp::KeyboardInputEvent& ev) {
    626     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    627          it != textfield_.end();
    628          ++it) {
    629       if (it->Focused()) {
    630         bool shift = ev.GetModifiers() & PP_INPUTEVENT_MODIFIER_SHIFTKEY;
    631         switch (ev.GetKeyCode()) {
    632           case VKEY_LEFT:
    633             it->KeyLeft(shift);
    634             break;
    635           case VKEY_RIGHT:
    636             it->KeyRight(shift);
    637             break;
    638           case VKEY_DELETE:
    639             it->KeyDelete();
    640             break;
    641           case VKEY_BACK:
    642             it->KeyBackspace();
    643             break;
    644         }
    645         return true;
    646       }
    647     }
    648     return false;
    649   }
    650 
    651   bool OnChar(const pp::KeyboardInputEvent& ev) {
    652     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    653          it != textfield_.end();
    654          ++it) {
    655       if (it->Focused()) {
    656         std::string str = ev.GetCharacterText().AsString();
    657         if (str != "\r" && str != "\n")
    658           it->InsertText(str);
    659         return true;
    660       }
    661     }
    662     return false;
    663   }
    664 
    665   bool OnImeText(const pp::IMEInputEvent ev) {
    666     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    667          it != textfield_.end();
    668          ++it) {
    669       if (it->Focused()) {
    670         it->InsertText(ev.GetText().AsString());
    671         return true;
    672       }
    673     }
    674     return false;
    675   }
    676 
    677   void Paint() {
    678     pp::Rect clip(0, 0, last_size_.width(), last_size_.height());
    679     PaintClip(clip);
    680   }
    681 
    682   void PaintClip(const pp::Rect& clip) {
    683     pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, last_size_, true);
    684     pp::Graphics2D device(this, last_size_, false);
    685     BindGraphics(device);
    686 
    687     for (std::vector<MyTextField>::iterator it = textfield_.begin();
    688          it != textfield_.end();
    689          ++it) {
    690       it->PaintOn(&image, clip);
    691     }
    692 
    693     device.PaintImageData(image, pp::Point(0, 0));
    694     device.Flush(pp::CompletionCallback(&OnFlush, this));
    695   }
    696 
    697   static void OnFlush(void* user_data, int32_t result) {}
    698 
    699   // Prints a debug message.
    700   void Log(const pp::Var& value) {
    701     const PPB_Console* console = reinterpret_cast<const PPB_Console*>(
    702         pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
    703     if (!console)
    704       return;
    705     console->Log(pp_instance(), PP_LOGLEVEL_LOG, value.pp_var());
    706   }
    707 
    708   // IME Control interface.
    709   TextFieldStatusHandler* status_handler_;
    710 
    711   // Remembers the size of this instance.
    712   pp::Size last_size_;
    713 
    714   // Holds instances of text fields.
    715   std::vector<MyTextField> textfield_;
    716 
    717   // Whether or not during a drag operation.
    718   bool dragging_;
    719 };
    720 
    721 class MyModule : public pp::Module {
    722   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    723     return new MyInstance(instance);
    724   }
    725 };
    726 
    727 namespace pp {
    728 
    729 Module* CreateModule() {
    730   return new MyModule();
    731 }
    732 
    733 }  // namespace pp
    734