Home | History | Annotate | Download | only in win
      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 #ifndef UI_BASE_IME_WIN_TSF_TEXT_STORE_H_
      6 #define UI_BASE_IME_WIN_TSF_TEXT_STORE_H_
      7 
      8 #include <msctf.h>
      9 #include <deque>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/strings/string16.h"
     14 #include "base/win/scoped_comptr.h"
     15 #include "ui/base/ime/composition_underline.h"
     16 #include "ui/base/ui_export.h"
     17 #include "ui/gfx/range/range.h"
     18 
     19 namespace ui {
     20 class TextInputClient;
     21 
     22 // TSFTextStore is used to interact with the input method via TSF manager.
     23 // TSFTextStore have a string buffer which is manipulated by TSF manager through
     24 // ITextStoreACP interface methods such as SetText().
     25 // When the input method updates the composition, TSFTextStore calls
     26 // TextInputClient::SetCompositionText(). And when the input method finishes the
     27 // composition, TSFTextStore calls TextInputClient::InsertText() and clears the
     28 // buffer.
     29 //
     30 // How TSFTextStore works:
     31 //  - The user enters "a".
     32 //    - The input method set composition as "a".
     33 //    - TSF manager calls TSFTextStore::RequestLock().
     34 //    - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted().
     35 //    - In OnLockGranted(), TSF manager calls
     36 //      - TSFTextStore::OnStartComposition()
     37 //      - TSFTextStore::SetText()
     38 //        The string buffer is set as "a".
     39 //      - TSFTextStore::OnUpdateComposition()
     40 //      - TSFTextStore::OnEndEdit()
     41 //        TSFTextStore can get the composition information such as underlines.
     42 //   - TSFTextStore calls TextInputClient::SetCompositionText().
     43 //     "a" is shown with an underline as composition string.
     44 // - The user enters <space>.
     45 //    - The input method set composition as "A".
     46 //    - TSF manager calls TSFTextStore::RequestLock().
     47 //    - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted().
     48 //    - In OnLockGranted(), TSF manager calls
     49 //      - TSFTextStore::SetText()
     50 //        The string buffer is set as "A".
     51 //      - TSFTextStore::OnUpdateComposition()
     52 //      - TSFTextStore::OnEndEdit()
     53 //   - TSFTextStore calls TextInputClient::SetCompositionText().
     54 //     "A" is shown with an underline as composition string.
     55 // - The user enters <enter>.
     56 //    - The input method commits "A".
     57 //    - TSF manager calls TSFTextStore::RequestLock().
     58 //    - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted().
     59 //    - In OnLockGranted(), TSF manager calls
     60 //      - TSFTextStore::OnEndComposition()
     61 //      - TSFTextStore::OnEndEdit()
     62 //        TSFTextStore knows "A" is committed.
     63 //   - TSFTextStore calls TextInputClient::InsertText().
     64 //     "A" is shown as committed string.
     65 //   - TSFTextStore clears the string buffer.
     66 //   - TSFTextStore calls OnSelectionChange(), OnLayoutChange() and
     67 //     OnTextChange() of ITextStoreACPSink to let TSF manager know that the
     68 //     string buffer has been changed.
     69 //
     70 // About the locking scheme:
     71 // When TSF manager manipulates the string buffer it calls RequestLock() to get
     72 // the lock of the document. If TSFTextStore can grant the lock request, it
     73 // callbacks ITextStoreACPSink::OnLockGranted().
     74 // RequestLock() is called from only one thread, but called recursively in
     75 // OnLockGranted() or OnSelectionChange() or OnLayoutChange() or OnTextChange().
     76 // If the document is locked and the lock request is asynchronous, TSFTextStore
     77 // queues the request. The queued requests will be handled after the current
     78 // lock is removed.
     79 // More information about document locks can be found here:
     80 //   http://msdn.microsoft.com/en-us/library/ms538064
     81 //
     82 // More information about TSF can be found here:
     83 //   http://msdn.microsoft.com/en-us/library/ms629032
     84 class UI_EXPORT TSFTextStore : public ITextStoreACP,
     85                                public ITfContextOwnerCompositionSink,
     86                                public ITfTextEditSink {
     87  public:
     88   TSFTextStore();
     89   virtual ~TSFTextStore();
     90 
     91   // ITextStoreACP:
     92   STDMETHOD_(ULONG, AddRef)() OVERRIDE;
     93   STDMETHOD_(ULONG, Release)() OVERRIDE;
     94   STDMETHOD(QueryInterface)(REFIID iid, void** ppv) OVERRIDE;
     95   STDMETHOD(AdviseSink)(REFIID iid, IUnknown* unknown, DWORD mask) OVERRIDE;
     96   STDMETHOD(FindNextAttrTransition)(LONG acp_start,
     97                                     LONG acp_halt,
     98                                     ULONG num_filter_attributes,
     99                                     const TS_ATTRID* filter_attributes,
    100                                     DWORD flags,
    101                                     LONG* acp_next,
    102                                     BOOL* found,
    103                                     LONG* found_offset) OVERRIDE;
    104   STDMETHOD(GetACPFromPoint)(TsViewCookie view_cookie,
    105                              const POINT* point,
    106                              DWORD flags,
    107                              LONG* acp) OVERRIDE;
    108   STDMETHOD(GetActiveView)(TsViewCookie* view_cookie) OVERRIDE;
    109   STDMETHOD(GetEmbedded)(LONG acp_pos,
    110                          REFGUID service,
    111                          REFIID iid,
    112                          IUnknown** unknown) OVERRIDE;
    113   STDMETHOD(GetEndACP)(LONG* acp) OVERRIDE;
    114   STDMETHOD(GetFormattedText)(LONG acp_start,
    115                               LONG acp_end,
    116                               IDataObject** data_object) OVERRIDE;
    117   STDMETHOD(GetScreenExt)(TsViewCookie view_cookie, RECT* rect) OVERRIDE;
    118   STDMETHOD(GetSelection)(ULONG selection_index,
    119                           ULONG selection_buffer_size,
    120                           TS_SELECTION_ACP* selection_buffer,
    121                           ULONG* fetched_count) OVERRIDE;
    122   STDMETHOD(GetStatus)(TS_STATUS* pdcs) OVERRIDE;
    123   STDMETHOD(GetText)(LONG acp_start,
    124                      LONG acp_end,
    125                      wchar_t* text_buffer,
    126                      ULONG text_buffer_size,
    127                      ULONG* text_buffer_copied,
    128                      TS_RUNINFO* run_info_buffer,
    129                      ULONG run_info_buffer_size,
    130                      ULONG* run_info_buffer_copied,
    131                      LONG* next_acp) OVERRIDE;
    132   STDMETHOD(GetTextExt)(TsViewCookie view_cookie,
    133                         LONG acp_start,
    134                         LONG acp_end,
    135                         RECT* rect,
    136                         BOOL* clipped) OVERRIDE;
    137   STDMETHOD(GetWnd)(TsViewCookie view_cookie, HWND* window_handle) OVERRIDE;
    138   STDMETHOD(InsertEmbedded)(DWORD flags,
    139                             LONG acp_start,
    140                             LONG acp_end,
    141                             IDataObject* data_object,
    142                             TS_TEXTCHANGE* change) OVERRIDE;
    143   STDMETHOD(InsertEmbeddedAtSelection)(DWORD flags,
    144                                        IDataObject* data_object,
    145                                        LONG* acp_start,
    146                                        LONG* acp_end,
    147                                        TS_TEXTCHANGE* change) OVERRIDE;
    148   STDMETHOD(InsertTextAtSelection)(DWORD flags,
    149                                    const wchar_t* text_buffer,
    150                                    ULONG text_buffer_size,
    151                                    LONG* acp_start,
    152                                    LONG* acp_end,
    153                                    TS_TEXTCHANGE* text_change) OVERRIDE;
    154   STDMETHOD(QueryInsert)(LONG acp_test_start,
    155                          LONG acp_test_end,
    156                          ULONG text_size,
    157                          LONG* acp_result_start,
    158                          LONG* acp_result_end) OVERRIDE;
    159   STDMETHOD(QueryInsertEmbedded)(const GUID* service,
    160                                  const FORMATETC* format,
    161                                  BOOL* insertable) OVERRIDE;
    162   STDMETHOD(RequestAttrsAtPosition)(LONG acp_pos,
    163                                     ULONG attribute_buffer_size,
    164                                     const TS_ATTRID* attribute_buffer,
    165                                     DWORD flags) OVERRIDE;
    166   STDMETHOD(RequestAttrsTransitioningAtPosition)(
    167       LONG acp_pos,
    168       ULONG attribute_buffer_size,
    169       const TS_ATTRID* attribute_buffer,
    170       DWORD flags) OVERRIDE;
    171   STDMETHOD(RequestLock)(DWORD lock_flags, HRESULT* result) OVERRIDE;
    172   STDMETHOD(RequestSupportedAttrs)(DWORD flags,
    173                                    ULONG attribute_buffer_size,
    174                                    const TS_ATTRID* attribute_buffer) OVERRIDE;
    175   STDMETHOD(RetrieveRequestedAttrs)(ULONG attribute_buffer_size,
    176                                     TS_ATTRVAL* attribute_buffer,
    177                                     ULONG* attribute_buffer_copied) OVERRIDE;
    178   STDMETHOD(SetSelection)(ULONG selection_buffer_size,
    179                           const TS_SELECTION_ACP* selection_buffer) OVERRIDE;
    180   STDMETHOD(SetText)(DWORD flags,
    181                      LONG acp_start,
    182                      LONG acp_end,
    183                      const wchar_t* text_buffer,
    184                      ULONG text_buffer_size,
    185                      TS_TEXTCHANGE* text_change) OVERRIDE;
    186   STDMETHOD(UnadviseSink)(IUnknown* unknown) OVERRIDE;
    187 
    188   // ITfContextOwnerCompositionSink:
    189   STDMETHOD(OnStartComposition)(ITfCompositionView* composition_view,
    190                                 BOOL* ok) OVERRIDE;
    191   STDMETHOD(OnUpdateComposition)(ITfCompositionView* composition_view,
    192                                  ITfRange* range) OVERRIDE;
    193   STDMETHOD(OnEndComposition)(ITfCompositionView* composition_view) OVERRIDE;
    194 
    195   // ITfTextEditSink:
    196   STDMETHOD(OnEndEdit)(ITfContext* context, TfEditCookie read_only_edit_cookie,
    197                        ITfEditRecord* edit_record) OVERRIDE;
    198 
    199   // Sets currently focused TextInputClient.
    200   void SetFocusedTextInputClient(HWND focused_window,
    201                                  TextInputClient* text_input_client);
    202   // Removes currently focused TextInputClient.
    203   void RemoveFocusedTextInputClient(TextInputClient* text_input_client);
    204 
    205   // Cancels the ongoing composition if exists.
    206   bool CancelComposition();
    207 
    208   // Confirms the ongoing composition if exists.
    209   bool ConfirmComposition();
    210 
    211   // Sends OnLayoutChange() via |text_store_acp_sink_|.
    212   void SendOnLayoutChange();
    213 
    214  private:
    215   friend class TSFTextStoreTest;
    216   friend class TSFTextStoreTestCallback;
    217 
    218   // Checks if the document has a read-only lock.
    219   bool HasReadLock() const;
    220 
    221   // Checks if the document has a read and write lock.
    222   bool HasReadWriteLock() const;
    223 
    224   // Gets the display attribute structure.
    225   bool GetDisplayAttribute(TfGuidAtom guid_atom,
    226                            TF_DISPLAYATTRIBUTE* attribute);
    227 
    228   // Gets the committed string size and underline information of the context.
    229   bool GetCompositionStatus(ITfContext* context,
    230                             const TfEditCookie read_only_edit_cookie,
    231                             size_t* committed_size,
    232                             CompositionUnderlines* undelines);
    233 
    234   // The refrence count of this instance.
    235   volatile LONG ref_count_;
    236 
    237   // A pointer of ITextStoreACPSink, this instance is given in AdviseSink.
    238   base::win::ScopedComPtr<ITextStoreACPSink> text_store_acp_sink_;
    239 
    240   // The current mask of |text_store_acp_sink_|.
    241   DWORD text_store_acp_sink_mask_;
    242 
    243   // HWND of the current view window which is set in SetFocusedTextInputClient.
    244   HWND window_handle_;
    245 
    246   // Current TextInputClient which is set in SetFocusedTextInputClient.
    247   TextInputClient* text_input_client_;
    248 
    249   //  |string_buffer_| contains committed string and composition string.
    250   //  Example: "aoi" is committed, and "umi" is under composition.
    251   //    |string_buffer_|: "aoiumi"
    252   //    |committed_size_|: 3
    253   string16 string_buffer_;
    254   size_t committed_size_;
    255 
    256   //  |selection_start_| and |selection_end_| indicates the selection range.
    257   //  Example: "iue" is selected
    258   //    |string_buffer_|: "aiueo"
    259   //    |selection_.start()|: 1
    260   //    |selection_.end()|: 4
    261   gfx::Range selection_;
    262 
    263   //  |start_offset| and |end_offset| of |composition_undelines_| indicates
    264   //  the offsets in |string_buffer_|.
    265   //  Example: "aoi" is committed. There are two underlines in "umi" and "no".
    266   //    |string_buffer_|: "aoiumino"
    267   //    |committed_size_|: 3
    268   //    composition_undelines_.underlines[0].start_offset: 3
    269   //    composition_undelines_.underlines[0].end_offset: 6
    270   //    composition_undelines_.underlines[1].start_offset: 6
    271   //    composition_undelines_.underlines[1].end_offset: 8
    272   CompositionUnderlines composition_undelines_;
    273 
    274   // |edit_flag_| indicates that the status is edited during
    275   // ITextStoreACPSink::OnLockGranted().
    276   bool edit_flag_;
    277 
    278   // The type of current lock.
    279   //   0: No lock.
    280   //   TS_LF_READ: read-only lock.
    281   //   TS_LF_READWRITE: read/write lock.
    282   DWORD current_lock_type_;
    283 
    284   // Queue of the lock request used in RequestLock().
    285   std::deque<DWORD> lock_queue_;
    286 
    287   // Category manager and Display attribute manager are used to obtain the
    288   // attributes of the composition string.
    289   base::win::ScopedComPtr<ITfCategoryMgr> category_manager_;
    290   base::win::ScopedComPtr<ITfDisplayAttributeMgr> display_attribute_manager_;
    291 
    292   DISALLOW_COPY_AND_ASSIGN(TSFTextStore);
    293 };
    294 
    295 }  // namespace ui
    296 
    297 #endif  // UI_BASE_IME_WIN_TSF_TEXT_STORE_H_
    298