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