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