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