OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #define INITGUID // required for GUID_PROP_INPUTSCOPE | 5 #define INITGUID // required for GUID_PROP_INPUTSCOPE |
6 #include "ui/base/ime/win/tsf_text_store.h" | 6 #include "win8/metro_driver/ime/text_store.h" |
7 | 7 |
8 #include <InputScope.h> | 8 #include <InputScope.h> |
9 #include <OleCtl.h> | 9 #include <OleCtl.h> |
10 | 10 |
11 #include <algorithm> | 11 #include <algorithm> |
12 | 12 |
13 #include "base/win/scoped_variant.h" | 13 #include "base/win/scoped_variant.h" |
14 #include "ui/base/ime/text_input_client.h" | 14 #include "win8/metro_driver/ime/input_scope.h" |
15 #include "ui/base/ime/win/tsf_input_scope.h" | 15 #include "win8/metro_driver/ime/text_store_delegate.h" |
16 #include "ui/gfx/rect.h" | |
17 | 16 |
18 namespace ui { | 17 namespace metro_driver { |
19 namespace { | 18 namespace { |
20 | 19 |
21 // We support only one view. | 20 // We support only one view. |
22 const TsViewCookie kViewCookie = 1; | 21 const TsViewCookie kViewCookie = 1; |
23 | 22 |
24 } // namespace | 23 } // namespace |
25 | 24 |
26 TSFTextStore::TSFTextStore() | 25 TextStore::TextStore(HWND window_handle, |
| 26 const std::vector<InputScope>& input_scopes, |
| 27 TextStoreDelegate* delegate) |
27 : ref_count_(0), | 28 : ref_count_(0), |
28 text_store_acp_sink_mask_(0), | 29 text_store_acp_sink_mask_(0), |
29 window_handle_(NULL), | 30 window_handle_(window_handle), |
30 text_input_client_(NULL), | 31 delegate_(delegate), |
31 committed_size_(0), | 32 committed_size_(0), |
| 33 selection_start_(0), |
| 34 selection_end_(0), |
32 edit_flag_(false), | 35 edit_flag_(false), |
33 current_lock_type_(0) { | 36 current_lock_type_(0) { |
34 if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) { | 37 if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) { |
35 LOG(FATAL) << "Failed to initialize CategoryMgr."; | 38 LOG(FATAL) << "Failed to initialize CategoryMgr."; |
36 return; | 39 return; |
37 } | 40 } |
38 if (FAILED(display_attribute_manager_.CreateInstance( | 41 if (FAILED(display_attribute_manager_.CreateInstance( |
39 CLSID_TF_DisplayAttributeMgr))) { | 42 CLSID_TF_DisplayAttributeMgr))) { |
40 LOG(FATAL) << "Failed to initialize DisplayAttributeMgr."; | 43 LOG(FATAL) << "Failed to initialize DisplayAttributeMgr."; |
41 return; | 44 return; |
42 } | 45 } |
| 46 input_scope_ = CreteInputScope(input_scopes); |
43 } | 47 } |
44 | 48 |
45 TSFTextStore::~TSFTextStore() { | 49 TextStore::~TextStore() { |
46 } | 50 } |
47 | 51 |
48 ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() { | 52 ULONG STDMETHODCALLTYPE TextStore::AddRef() { |
49 return InterlockedIncrement(&ref_count_); | 53 return InterlockedIncrement(&ref_count_); |
50 } | 54 } |
51 | 55 |
52 ULONG STDMETHODCALLTYPE TSFTextStore::Release() { | 56 ULONG STDMETHODCALLTYPE TextStore::Release() { |
53 const LONG count = InterlockedDecrement(&ref_count_); | 57 const LONG count = InterlockedDecrement(&ref_count_); |
54 if (!count) { | 58 if (!count) { |
55 delete this; | 59 delete this; |
56 return 0; | 60 return 0; |
57 } | 61 } |
58 return static_cast<ULONG>(count); | 62 return static_cast<ULONG>(count); |
59 } | 63 } |
60 | 64 |
61 STDMETHODIMP TSFTextStore::QueryInterface(REFIID iid, void** result) { | 65 STDMETHODIMP TextStore::QueryInterface(REFIID iid, void** result) { |
62 if (iid == IID_IUnknown || iid == IID_ITextStoreACP) { | 66 if (iid == IID_IUnknown || iid == IID_ITextStoreACP) { |
63 *result = static_cast<ITextStoreACP*>(this); | 67 *result = static_cast<ITextStoreACP*>(this); |
64 } else if (iid == IID_ITfContextOwnerCompositionSink) { | 68 } else if (iid == IID_ITfContextOwnerCompositionSink) { |
65 *result = static_cast<ITfContextOwnerCompositionSink*>(this); | 69 *result = static_cast<ITfContextOwnerCompositionSink*>(this); |
66 } else if (iid == IID_ITfTextEditSink) { | 70 } else if (iid == IID_ITfTextEditSink) { |
67 *result = static_cast<ITfTextEditSink*>(this); | 71 *result = static_cast<ITfTextEditSink*>(this); |
68 } else { | 72 } else { |
69 *result = NULL; | 73 *result = NULL; |
70 return E_NOINTERFACE; | 74 return E_NOINTERFACE; |
71 } | 75 } |
72 AddRef(); | 76 AddRef(); |
73 return S_OK; | 77 return S_OK; |
74 } | 78 } |
75 | 79 |
76 STDMETHODIMP TSFTextStore::AdviseSink(REFIID iid, | 80 STDMETHODIMP TextStore::AdviseSink(REFIID iid, |
77 IUnknown* unknown, | 81 IUnknown* unknown, |
78 DWORD mask) { | 82 DWORD mask) { |
79 if (!IsEqualGUID(iid, IID_ITextStoreACPSink)) | 83 if (!IsEqualGUID(iid, IID_ITextStoreACPSink)) |
80 return E_INVALIDARG; | 84 return E_INVALIDARG; |
81 if (text_store_acp_sink_) { | 85 if (text_store_acp_sink_) { |
82 if (text_store_acp_sink_.IsSameObject(unknown)) { | 86 if (text_store_acp_sink_.IsSameObject(unknown)) { |
83 text_store_acp_sink_mask_ = mask; | 87 text_store_acp_sink_mask_ = mask; |
84 return S_OK; | 88 return S_OK; |
85 } else { | 89 } else { |
86 return CONNECT_E_ADVISELIMIT; | 90 return CONNECT_E_ADVISELIMIT; |
87 } | 91 } |
88 } | 92 } |
89 if (FAILED(text_store_acp_sink_.QueryFrom(unknown))) | 93 if (FAILED(text_store_acp_sink_.QueryFrom(unknown))) |
90 return E_UNEXPECTED; | 94 return E_UNEXPECTED; |
91 text_store_acp_sink_mask_ = mask; | 95 text_store_acp_sink_mask_ = mask; |
92 | 96 |
93 return S_OK; | 97 return S_OK; |
94 } | 98 } |
95 | 99 |
96 STDMETHODIMP TSFTextStore::FindNextAttrTransition( | 100 STDMETHODIMP TextStore::FindNextAttrTransition( |
97 LONG acp_start, | 101 LONG acp_start, |
98 LONG acp_halt, | 102 LONG acp_halt, |
99 ULONG num_filter_attributes, | 103 ULONG num_filter_attributes, |
100 const TS_ATTRID* filter_attributes, | 104 const TS_ATTRID* filter_attributes, |
101 DWORD flags, | 105 DWORD flags, |
102 LONG* acp_next, | 106 LONG* acp_next, |
103 BOOL* found, | 107 BOOL* found, |
104 LONG* found_offset) { | 108 LONG* found_offset) { |
105 if (!acp_next || !found || !found_offset) | 109 if (!acp_next || !found || !found_offset) |
106 return E_INVALIDARG; | 110 return E_INVALIDARG; |
107 // We don't support any attributes. | 111 // We don't support any attributes. |
108 // So we always return "not found". | 112 // So we always return "not found". |
109 *acp_next = 0; | 113 *acp_next = 0; |
110 *found = FALSE; | 114 *found = FALSE; |
111 *found_offset = 0; | 115 *found_offset = 0; |
112 return S_OK; | 116 return S_OK; |
113 } | 117 } |
114 | 118 |
115 STDMETHODIMP TSFTextStore::GetACPFromPoint(TsViewCookie view_cookie, | 119 STDMETHODIMP TextStore::GetACPFromPoint(TsViewCookie view_cookie, |
116 const POINT* point, | 120 const POINT* point, |
117 DWORD flags, | 121 DWORD flags, |
118 LONG* acp) { | 122 LONG* acp) { |
119 NOTIMPLEMENTED(); | 123 NOTIMPLEMENTED(); |
120 if (view_cookie != kViewCookie) | 124 if (view_cookie != kViewCookie) |
121 return E_INVALIDARG; | 125 return E_INVALIDARG; |
122 return E_NOTIMPL; | 126 return E_NOTIMPL; |
123 } | 127 } |
124 | 128 |
125 STDMETHODIMP TSFTextStore::GetActiveView(TsViewCookie* view_cookie) { | 129 STDMETHODIMP TextStore::GetActiveView(TsViewCookie* view_cookie) { |
126 if (!view_cookie) | 130 if (!view_cookie) |
127 return E_INVALIDARG; | 131 return E_INVALIDARG; |
128 // We support only one view. | 132 // We support only one view. |
129 *view_cookie = kViewCookie; | 133 *view_cookie = kViewCookie; |
130 return S_OK; | 134 return S_OK; |
131 } | 135 } |
132 | 136 |
133 STDMETHODIMP TSFTextStore::GetEmbedded(LONG acp_pos, | 137 STDMETHODIMP TextStore::GetEmbedded(LONG acp_pos, |
134 REFGUID service, | 138 REFGUID service, |
135 REFIID iid, | 139 REFIID iid, |
136 IUnknown** unknown) { | 140 IUnknown** unknown) { |
137 // We don't support any embedded objects. | 141 // We don't support any embedded objects. |
138 NOTIMPLEMENTED(); | 142 NOTIMPLEMENTED(); |
139 if (!unknown) | 143 if (!unknown) |
140 return E_INVALIDARG; | 144 return E_INVALIDARG; |
141 *unknown = NULL; | 145 *unknown = NULL; |
142 return E_NOTIMPL; | 146 return E_NOTIMPL; |
143 } | 147 } |
144 | 148 |
145 STDMETHODIMP TSFTextStore::GetEndACP(LONG* acp) { | 149 STDMETHODIMP TextStore::GetEndACP(LONG* acp) { |
146 if (!acp) | 150 if (!acp) |
147 return E_INVALIDARG; | 151 return E_INVALIDARG; |
148 if (!HasReadLock()) | 152 if (!HasReadLock()) |
149 return TS_E_NOLOCK; | 153 return TS_E_NOLOCK; |
150 *acp = string_buffer_.size(); | 154 *acp = string_buffer_.size(); |
151 return S_OK; | 155 return S_OK; |
152 } | 156 } |
153 | 157 |
154 STDMETHODIMP TSFTextStore::GetFormattedText(LONG acp_start, LONG acp_end, | 158 STDMETHODIMP TextStore::GetFormattedText(LONG acp_start, LONG acp_end, |
155 IDataObject** data_object) { | 159 IDataObject** data_object) { |
156 NOTIMPLEMENTED(); | 160 NOTIMPLEMENTED(); |
157 return E_NOTIMPL; | 161 return E_NOTIMPL; |
158 } | 162 } |
159 | 163 |
160 STDMETHODIMP TSFTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { | 164 STDMETHODIMP TextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { |
161 if (view_cookie != kViewCookie) | 165 if (view_cookie != kViewCookie) |
162 return E_INVALIDARG; | 166 return E_INVALIDARG; |
163 if (!rect) | 167 if (!rect) |
164 return E_INVALIDARG; | 168 return E_INVALIDARG; |
165 | 169 |
166 // {0, 0, 0, 0} means that the document rect is not currently displayed. | 170 // {0, 0, 0, 0} means that the document rect is not currently displayed. |
167 SetRect(rect, 0, 0, 0, 0); | 171 SetRect(rect, 0, 0, 0, 0); |
168 | 172 |
169 if (!IsWindow(window_handle_)) | |
170 return E_FAIL; | |
171 | |
172 // Currently ui::TextInputClient does not expose the document rect. So use | |
173 // the Win32 client rectangle instead. | |
174 // TODO(yukawa): Upgrade TextInputClient so that the client can retrieve the | |
175 // document rectangle. | |
176 RECT client_rect = {}; | 173 RECT client_rect = {}; |
177 if (!GetClientRect(window_handle_, &client_rect)) | 174 if (!GetClientRect(window_handle_, &client_rect)) |
178 return E_FAIL; | 175 return E_FAIL; |
179 POINT left_top = {client_rect.left, client_rect.top}; | 176 POINT left_top = {client_rect.left, client_rect.top}; |
180 POINT right_bottom = {client_rect.right, client_rect.bottom}; | 177 POINT right_bottom = {client_rect.right, client_rect.bottom}; |
181 if (!ClientToScreen(window_handle_, &left_top)) | 178 if (!ClientToScreen(window_handle_, &left_top)) |
182 return E_FAIL; | 179 return E_FAIL; |
183 if (!ClientToScreen(window_handle_, &right_bottom)) | 180 if (!ClientToScreen(window_handle_, &right_bottom)) |
184 return E_FAIL; | 181 return E_FAIL; |
185 | 182 |
186 rect->left = left_top.x; | 183 rect->left = left_top.x; |
187 rect->top = left_top.y; | 184 rect->top = left_top.y; |
188 rect->right = right_bottom.x; | 185 rect->right = right_bottom.x; |
189 rect->bottom = right_bottom.y; | 186 rect->bottom = right_bottom.y; |
190 return S_OK; | 187 return S_OK; |
191 } | 188 } |
192 | 189 |
193 STDMETHODIMP TSFTextStore::GetSelection(ULONG selection_index, | 190 STDMETHODIMP TextStore::GetSelection(ULONG selection_index, |
194 ULONG selection_buffer_size, | 191 ULONG selection_buffer_size, |
195 TS_SELECTION_ACP* selection_buffer, | 192 TS_SELECTION_ACP* selection_buffer, |
196 ULONG* fetched_count) { | 193 ULONG* fetched_count) { |
197 if (!selection_buffer) | 194 if (!selection_buffer) |
198 return E_INVALIDARG; | 195 return E_INVALIDARG; |
199 if (!fetched_count) | 196 if (!fetched_count) |
200 return E_INVALIDARG; | 197 return E_INVALIDARG; |
201 if (!HasReadLock()) | 198 if (!HasReadLock()) |
202 return TS_E_NOLOCK; | 199 return TS_E_NOLOCK; |
203 *fetched_count = 0; | 200 *fetched_count = 0; |
204 if ((selection_buffer_size > 0) && | 201 if ((selection_buffer_size > 0) && |
205 ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) { | 202 ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) { |
206 selection_buffer[0].acpStart = selection_.start(); | 203 selection_buffer[0].acpStart = selection_start_; |
207 selection_buffer[0].acpEnd = selection_.end(); | 204 selection_buffer[0].acpEnd = selection_end_; |
208 selection_buffer[0].style.ase = TS_AE_END; | 205 selection_buffer[0].style.ase = TS_AE_END; |
209 selection_buffer[0].style.fInterimChar = FALSE; | 206 selection_buffer[0].style.fInterimChar = FALSE; |
210 *fetched_count = 1; | 207 *fetched_count = 1; |
211 } | 208 } |
212 return S_OK; | 209 return S_OK; |
213 } | 210 } |
214 | 211 |
215 STDMETHODIMP TSFTextStore::GetStatus(TS_STATUS* status) { | 212 STDMETHODIMP TextStore::GetStatus(TS_STATUS* status) { |
216 if (!status) | 213 if (!status) |
217 return E_INVALIDARG; | 214 return E_INVALIDARG; |
218 | 215 |
219 status->dwDynamicFlags = 0; | 216 status->dwDynamicFlags = 0; |
220 // We use transitory contexts and we don't support hidden text. | 217 // We use transitory contexts and we don't support hidden text. |
221 status->dwStaticFlags = TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT; | 218 status->dwStaticFlags = TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT; |
222 | 219 |
223 return S_OK; | 220 return S_OK; |
224 } | 221 } |
225 | 222 |
226 STDMETHODIMP TSFTextStore::GetText(LONG acp_start, | 223 STDMETHODIMP TextStore::GetText(LONG acp_start, |
227 LONG acp_end, | 224 LONG acp_end, |
228 wchar_t* text_buffer, | 225 wchar_t* text_buffer, |
229 ULONG text_buffer_size, | 226 ULONG text_buffer_size, |
230 ULONG* text_buffer_copied, | 227 ULONG* text_buffer_copied, |
231 TS_RUNINFO* run_info_buffer, | 228 TS_RUNINFO* run_info_buffer, |
232 ULONG run_info_buffer_size, | 229 ULONG run_info_buffer_size, |
233 ULONG* run_info_buffer_copied, | 230 ULONG* run_info_buffer_copied, |
234 LONG* next_acp) { | 231 LONG* next_acp) { |
235 if (!text_buffer_copied || !run_info_buffer_copied) | 232 if (!text_buffer_copied || !run_info_buffer_copied) |
236 return E_INVALIDARG; | 233 return E_INVALIDARG; |
237 if (!text_buffer && text_buffer_size != 0) | 234 if (!text_buffer && text_buffer_size != 0) |
238 return E_INVALIDARG; | 235 return E_INVALIDARG; |
239 if (!run_info_buffer && run_info_buffer_size != 0) | 236 if (!run_info_buffer && run_info_buffer_size != 0) |
240 return E_INVALIDARG; | 237 return E_INVALIDARG; |
241 if (!next_acp) | 238 if (!next_acp) |
242 return E_INVALIDARG; | 239 return E_INVALIDARG; |
243 if (!HasReadLock()) | 240 if (!HasReadLock()) |
244 return TF_E_NOLOCK; | 241 return TF_E_NOLOCK; |
(...skipping 17 matching lines...) Expand all Loading... |
262 if (run_info_buffer_size) { | 259 if (run_info_buffer_size) { |
263 run_info_buffer[0].uCount = *text_buffer_copied; | 260 run_info_buffer[0].uCount = *text_buffer_copied; |
264 run_info_buffer[0].type = TS_RT_PLAIN; | 261 run_info_buffer[0].type = TS_RT_PLAIN; |
265 *run_info_buffer_copied = 1; | 262 *run_info_buffer_copied = 1; |
266 } | 263 } |
267 | 264 |
268 *next_acp = acp_end; | 265 *next_acp = acp_end; |
269 return S_OK; | 266 return S_OK; |
270 } | 267 } |
271 | 268 |
272 STDMETHODIMP TSFTextStore::GetTextExt(TsViewCookie view_cookie, | 269 STDMETHODIMP TextStore::GetTextExt(TsViewCookie view_cookie, |
273 LONG acp_start, | 270 LONG acp_start, |
274 LONG acp_end, | 271 LONG acp_end, |
275 RECT* rect, | 272 RECT* rect, |
276 BOOL* clipped) { | 273 BOOL* clipped) { |
277 if (!rect || !clipped) | 274 if (!rect || !clipped) |
278 return E_INVALIDARG; | 275 return E_INVALIDARG; |
279 if (!text_input_client_) | |
280 return E_UNEXPECTED; | |
281 if (view_cookie != kViewCookie) | 276 if (view_cookie != kViewCookie) |
282 return E_INVALIDARG; | 277 return E_INVALIDARG; |
283 if (!HasReadLock()) | 278 if (!HasReadLock()) |
284 return TS_E_NOLOCK; | 279 return TS_E_NOLOCK; |
285 if (!((static_cast<LONG>(committed_size_) <= acp_start) && | 280 if (!((static_cast<LONG>(committed_size_) <= acp_start) && |
286 (acp_start <= acp_end) && | 281 (acp_start <= acp_end) && |
287 (acp_end <= static_cast<LONG>(string_buffer_.size())))) { | 282 (acp_end <= static_cast<LONG>(string_buffer_.size())))) { |
288 return TS_E_INVALIDPOS; | 283 return TS_E_INVALIDPOS; |
289 } | 284 } |
290 | 285 |
291 // According to a behavior of notepad.exe and wordpad.exe, top left corner of | 286 // According to a behavior of notepad.exe and wordpad.exe, top left corner of |
292 // rect indicates a first character's one, and bottom right corner of rect | 287 // rect indicates a first character's one, and bottom right corner of rect |
293 // indicates a last character's one. | 288 // indicates a last character's one. |
294 // We use RECT instead of gfx::Rect since left position may be bigger than | 289 // We use RECT instead of gfx::Rect since left position may be bigger than |
295 // right position when composition has multiple lines. | 290 // right position when composition has multiple lines. |
296 RECT result; | 291 RECT result = {}; |
297 gfx::Rect tmp_rect; | 292 RECT tmp_rect = {}; |
298 const uint32 start_pos = acp_start - committed_size_; | 293 const uint32 start_pos = acp_start - committed_size_; |
299 const uint32 end_pos = acp_end - committed_size_; | 294 const uint32 end_pos = acp_end - committed_size_; |
300 | 295 |
301 if (start_pos == end_pos) { | 296 if (start_pos == end_pos) { |
302 // According to MSDN document, if |acp_start| and |acp_end| are equal it is | 297 // According to MSDN document, if |acp_start| and |acp_end| are equal it is |
303 // OK to just return E_INVALIDARG. | 298 // OK to just return E_INVALIDARG. |
304 // http://msdn.microsoft.com/en-us/library/ms538435 | 299 // http://msdn.microsoft.com/en-us/library/ms538435 |
305 // But when using Pinin IME of Windows 8, this method is called with the | 300 // But when using Pinin IME of Windows 8, this method is called with the |
306 // equal values of |acp_start| and |acp_end|. So we handle this condition. | 301 // equal values of |acp_start| and |acp_end|. So we handle this condition. |
307 if (start_pos == 0) { | 302 if (start_pos == 0) { |
308 if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) { | 303 if (delegate_->GetCompositionCharacterBounds(0, &tmp_rect)) { |
309 tmp_rect.set_width(0); | 304 tmp_rect.right = tmp_rect.right; |
310 result = tmp_rect.ToRECT(); | 305 result = tmp_rect; |
311 } else if (string_buffer_.size() == committed_size_) { | 306 } else if (string_buffer_.size() == committed_size_) { |
312 result = text_input_client_->GetCaretBounds().ToRECT(); | 307 result = delegate_->GetCaretBounds(); |
313 } else { | 308 } else { |
314 return TS_E_NOLAYOUT; | 309 return TS_E_NOLAYOUT; |
315 } | 310 } |
316 } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1, | 311 } else if (delegate_->GetCompositionCharacterBounds(start_pos - 1, |
317 &tmp_rect)) { | 312 &tmp_rect)) { |
318 result.left = tmp_rect.right(); | 313 tmp_rect.left = tmp_rect.right; |
319 result.right = tmp_rect.right(); | 314 result = tmp_rect; |
320 result.top = tmp_rect.y(); | |
321 result.bottom = tmp_rect.bottom(); | |
322 } else { | 315 } else { |
323 return TS_E_NOLAYOUT; | 316 return TS_E_NOLAYOUT; |
324 } | 317 } |
325 } else { | 318 } else { |
326 if (text_input_client_->GetCompositionCharacterBounds(start_pos, | 319 if (delegate_->GetCompositionCharacterBounds(start_pos, &tmp_rect)) { |
327 &tmp_rect)) { | 320 result = tmp_rect; |
328 result.left = tmp_rect.x(); | 321 if (delegate_->GetCompositionCharacterBounds(end_pos - 1, &tmp_rect)) { |
329 result.top = tmp_rect.y(); | 322 result.right = tmp_rect.right; |
330 result.right = tmp_rect.right(); | 323 result.bottom = tmp_rect.bottom; |
331 result.bottom = tmp_rect.bottom(); | |
332 if (text_input_client_->GetCompositionCharacterBounds(end_pos - 1, | |
333 &tmp_rect)) { | |
334 result.right = tmp_rect.right(); | |
335 result.bottom = tmp_rect.bottom(); | |
336 } else { | 324 } else { |
337 // We may not be able to get the last character bounds, so we use the | 325 // We may not be able to get the last character bounds, so we use the |
338 // first character bounds instead of returning TS_E_NOLAYOUT. | 326 // first character bounds instead of returning TS_E_NOLAYOUT. |
339 } | 327 } |
340 } else { | 328 } else { |
341 // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so | 329 // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so |
342 // it's better to return previous caret rectangle instead. | 330 // it's better to return previous caret rectangle instead. |
343 // TODO(nona, kinaba): Remove this hack. | 331 // TODO(nona, kinaba): Remove this hack. |
344 if (start_pos == 0) { | 332 if (start_pos == 0) { |
345 result = text_input_client_->GetCaretBounds().ToRECT(); | 333 result = delegate_->GetCaretBounds(); |
346 } else { | 334 } else { |
347 return TS_E_NOLAYOUT; | 335 return TS_E_NOLAYOUT; |
348 } | 336 } |
349 } | 337 } |
350 } | 338 } |
351 | 339 |
352 *rect = result; | 340 *rect = result; |
353 *clipped = FALSE; | 341 *clipped = FALSE; |
354 return S_OK; | 342 return S_OK; |
355 } | 343 } |
356 | 344 |
357 STDMETHODIMP TSFTextStore::GetWnd(TsViewCookie view_cookie, | 345 STDMETHODIMP TextStore::GetWnd(TsViewCookie view_cookie, |
358 HWND* window_handle) { | 346 HWND* window_handle) { |
359 if (!window_handle) | 347 if (!window_handle) |
360 return E_INVALIDARG; | 348 return E_INVALIDARG; |
361 if (view_cookie != kViewCookie) | 349 if (view_cookie != kViewCookie) |
362 return E_INVALIDARG; | 350 return E_INVALIDARG; |
363 *window_handle = window_handle_; | 351 *window_handle = window_handle_; |
364 return S_OK; | 352 return S_OK; |
365 } | 353 } |
366 | 354 |
367 STDMETHODIMP TSFTextStore::InsertEmbedded(DWORD flags, | 355 STDMETHODIMP TextStore::InsertEmbedded(DWORD flags, |
368 LONG acp_start, | 356 LONG acp_start, |
369 LONG acp_end, | 357 LONG acp_end, |
370 IDataObject* data_object, | 358 IDataObject* data_object, |
371 TS_TEXTCHANGE* change) { | 359 TS_TEXTCHANGE* change) { |
372 // We don't support any embedded objects. | 360 // We don't support any embedded objects. |
373 NOTIMPLEMENTED(); | 361 NOTIMPLEMENTED(); |
374 return E_NOTIMPL; | 362 return E_NOTIMPL; |
375 } | 363 } |
376 | 364 |
377 STDMETHODIMP TSFTextStore::InsertEmbeddedAtSelection(DWORD flags, | 365 STDMETHODIMP TextStore::InsertEmbeddedAtSelection(DWORD flags, |
378 IDataObject* data_object, | 366 IDataObject* data_object, |
379 LONG* acp_start, | 367 LONG* acp_start, |
380 LONG* acp_end, | 368 LONG* acp_end, |
381 TS_TEXTCHANGE* change) { | 369 TS_TEXTCHANGE* change) { |
382 // We don't support any embedded objects. | 370 // We don't support any embedded objects. |
383 NOTIMPLEMENTED(); | 371 NOTIMPLEMENTED(); |
384 return E_NOTIMPL; | 372 return E_NOTIMPL; |
385 } | 373 } |
386 | 374 |
387 STDMETHODIMP TSFTextStore::InsertTextAtSelection(DWORD flags, | 375 STDMETHODIMP TextStore::InsertTextAtSelection(DWORD flags, |
388 const wchar_t* text_buffer, | 376 const wchar_t* text_buffer, |
389 ULONG text_buffer_size, | 377 ULONG text_buffer_size, |
390 LONG* acp_start, | 378 LONG* acp_start, |
391 LONG* acp_end, | 379 LONG* acp_end, |
392 TS_TEXTCHANGE* text_change) { | 380 TS_TEXTCHANGE* text_change) { |
393 const LONG start_pos = selection_.start(); | 381 const LONG start_pos = selection_start_; |
394 const LONG end_pos = selection_.end(); | 382 const LONG end_pos = selection_end_; |
395 const LONG new_end_pos = start_pos + text_buffer_size; | 383 const LONG new_end_pos = start_pos + text_buffer_size; |
396 | 384 |
397 if (flags & TS_IAS_QUERYONLY) { | 385 if (flags & TS_IAS_QUERYONLY) { |
398 if (!HasReadLock()) | 386 if (!HasReadLock()) |
399 return TS_E_NOLOCK; | 387 return TS_E_NOLOCK; |
400 if (acp_start) | 388 if (acp_start) |
401 *acp_start = start_pos; | 389 *acp_start = start_pos; |
402 if (acp_end) { | 390 if (acp_end) |
403 *acp_end = end_pos; | 391 *acp_end = end_pos; |
404 } | |
405 return S_OK; | 392 return S_OK; |
406 } | 393 } |
407 | 394 |
408 if (!HasReadWriteLock()) | 395 if (!HasReadWriteLock()) |
409 return TS_E_NOLOCK; | 396 return TS_E_NOLOCK; |
410 if (!text_buffer) | 397 if (!text_buffer) |
411 return E_INVALIDARG; | 398 return E_INVALIDARG; |
412 | 399 |
413 DCHECK_LE(start_pos, end_pos); | 400 DCHECK_LE(start_pos, end_pos); |
414 string_buffer_ = string_buffer_.substr(0, start_pos) + | 401 string_buffer_ = string_buffer_.substr(0, start_pos) + |
415 string16(text_buffer, text_buffer + text_buffer_size) + | 402 string16(text_buffer, text_buffer + text_buffer_size) + |
416 string_buffer_.substr(end_pos); | 403 string_buffer_.substr(end_pos); |
417 if (acp_start) | 404 if (acp_start) |
418 *acp_start = start_pos; | 405 *acp_start = start_pos; |
419 if (acp_end) | 406 if (acp_end) |
420 *acp_end = new_end_pos; | 407 *acp_end = new_end_pos; |
421 if (text_change) { | 408 if (text_change) { |
422 text_change->acpStart = start_pos; | 409 text_change->acpStart = start_pos; |
423 text_change->acpOldEnd = end_pos; | 410 text_change->acpOldEnd = end_pos; |
424 text_change->acpNewEnd = new_end_pos; | 411 text_change->acpNewEnd = new_end_pos; |
425 } | 412 } |
426 selection_.set_start(start_pos); | 413 selection_start_ = start_pos; |
427 selection_.set_end(new_end_pos); | 414 selection_end_ = new_end_pos; |
428 return S_OK; | 415 return S_OK; |
429 } | 416 } |
430 | 417 |
431 STDMETHODIMP TSFTextStore::QueryInsert( | 418 STDMETHODIMP TextStore::QueryInsert( |
432 LONG acp_test_start, | 419 LONG acp_test_start, |
433 LONG acp_test_end, | 420 LONG acp_test_end, |
434 ULONG text_size, | 421 ULONG text_size, |
435 LONG* acp_result_start, | 422 LONG* acp_result_start, |
436 LONG* acp_result_end) { | 423 LONG* acp_result_end) { |
437 if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end) | 424 if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end) |
438 return E_INVALIDARG; | 425 return E_INVALIDARG; |
439 const LONG committed_size = static_cast<LONG>(committed_size_); | 426 const LONG committed_size = static_cast<LONG>(committed_size_); |
440 const LONG buffer_size = static_cast<LONG>(string_buffer_.size()); | 427 const LONG buffer_size = static_cast<LONG>(string_buffer_.size()); |
441 *acp_result_start = std::min(std::max(committed_size, acp_test_start), | 428 *acp_result_start = std::min(std::max(committed_size, acp_test_start), |
442 buffer_size); | 429 buffer_size); |
443 *acp_result_end = std::min(std::max(committed_size, acp_test_end), | 430 *acp_result_end = std::min(std::max(committed_size, acp_test_end), |
444 buffer_size); | 431 buffer_size); |
445 return S_OK; | 432 return S_OK; |
446 } | 433 } |
447 | 434 |
448 STDMETHODIMP TSFTextStore::QueryInsertEmbedded(const GUID* service, | 435 STDMETHODIMP TextStore::QueryInsertEmbedded(const GUID* service, |
449 const FORMATETC* format, | 436 const FORMATETC* format, |
450 BOOL* insertable) { | 437 BOOL* insertable) { |
451 if (!format) | 438 if (!format) |
452 return E_INVALIDARG; | 439 return E_INVALIDARG; |
453 // We don't support any embedded objects. | 440 // We don't support any embedded objects. |
454 if (insertable) | 441 if (insertable) |
455 *insertable = FALSE; | 442 *insertable = FALSE; |
456 return S_OK; | 443 return S_OK; |
457 } | 444 } |
458 | 445 |
459 STDMETHODIMP TSFTextStore::RequestAttrsAtPosition( | 446 STDMETHODIMP TextStore::RequestAttrsAtPosition( |
460 LONG acp_pos, | 447 LONG acp_pos, |
461 ULONG attribute_buffer_size, | 448 ULONG attribute_buffer_size, |
462 const TS_ATTRID* attribute_buffer, | 449 const TS_ATTRID* attribute_buffer, |
463 DWORD flags) { | 450 DWORD flags) { |
464 // We don't support any document attributes. | 451 // We don't support any document attributes. |
465 // This method just returns S_OK, and the subsequently called | 452 // This method just returns S_OK, and the subsequently called |
466 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. | 453 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. |
467 return S_OK; | 454 return S_OK; |
468 } | 455 } |
469 | 456 |
470 STDMETHODIMP TSFTextStore::RequestAttrsTransitioningAtPosition( | 457 STDMETHODIMP TextStore::RequestAttrsTransitioningAtPosition( |
471 LONG acp_pos, | 458 LONG acp_pos, |
472 ULONG attribute_buffer_size, | 459 ULONG attribute_buffer_size, |
473 const TS_ATTRID* attribute_buffer, | 460 const TS_ATTRID* attribute_buffer, |
474 DWORD flags) { | 461 DWORD flags) { |
475 // We don't support any document attributes. | 462 // We don't support any document attributes. |
476 // This method just returns S_OK, and the subsequently called | 463 // This method just returns S_OK, and the subsequently called |
477 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. | 464 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. |
478 return S_OK; | 465 return S_OK; |
479 } | 466 } |
480 | 467 |
481 STDMETHODIMP TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { | 468 STDMETHODIMP TextStore::RequestLock(DWORD lock_flags, HRESULT* result) { |
482 if (!text_store_acp_sink_.get()) | 469 if (!text_store_acp_sink_.get()) |
483 return E_FAIL; | 470 return E_FAIL; |
484 if (!result) | 471 if (!result) |
485 return E_INVALIDARG; | 472 return E_INVALIDARG; |
486 | 473 |
487 if (current_lock_type_ != 0) { | 474 if (current_lock_type_ != 0) { |
488 if (lock_flags & TS_LF_SYNC) { | 475 if (lock_flags & TS_LF_SYNC) { |
489 // Can't lock synchronously. | 476 // Can't lock synchronously. |
490 *result = TS_E_SYNCHRONOUS; | 477 *result = TS_E_SYNCHRONOUS; |
491 return S_OK; | 478 return S_OK; |
(...skipping 17 matching lines...) Expand all Loading... |
509 current_lock_type_ = 0; | 496 current_lock_type_ = 0; |
510 | 497 |
511 // Handles the pending lock requests. | 498 // Handles the pending lock requests. |
512 while (!lock_queue_.empty()) { | 499 while (!lock_queue_.empty()) { |
513 current_lock_type_ = lock_queue_.front(); | 500 current_lock_type_ = lock_queue_.front(); |
514 lock_queue_.pop_front(); | 501 lock_queue_.pop_front(); |
515 text_store_acp_sink_->OnLockGranted(current_lock_type_); | 502 text_store_acp_sink_->OnLockGranted(current_lock_type_); |
516 current_lock_type_ = 0; | 503 current_lock_type_ = 0; |
517 } | 504 } |
518 | 505 |
519 if (!edit_flag_) { | 506 if (!edit_flag_) |
520 return S_OK; | 507 return S_OK; |
521 } | |
522 | 508 |
523 // If the text store is edited in OnLockGranted(), we may need to call | 509 // If the text store is edited in OnLockGranted(), we may need to call |
524 // TextInputClient::InsertText() or TextInputClient::SetCompositionText(). | 510 // TextStoreDelegate::ConfirmComposition() or |
| 511 // TextStoreDelegate::SetComposition(). |
525 const size_t new_committed_size = committed_size_; | 512 const size_t new_committed_size = committed_size_; |
526 const string16& new_committed_string = | 513 const string16& new_committed_string = |
527 string_buffer_.substr(last_committed_size, | 514 string_buffer_.substr(last_committed_size, |
528 new_committed_size - last_committed_size); | 515 new_committed_size - last_committed_size); |
529 const string16& composition_string = | 516 const string16& composition_string = |
530 string_buffer_.substr(new_committed_size); | 517 string_buffer_.substr(new_committed_size); |
531 | 518 |
532 // If there is new committed string, calls TextInputClient::InsertText(). | 519 if (delegate_) { |
533 if ((!new_committed_string.empty()) && text_input_client_) { | 520 // If there is new committed string, calls |
534 text_input_client_->InsertText(new_committed_string); | 521 // TextStoreDelegate::ConfirmComposition(). |
| 522 if ((!new_committed_string.empty())) |
| 523 delegate_->OnTextCommitted(new_committed_string); |
| 524 |
| 525 // Calls TextInputClient::SetCompositionText(). |
| 526 std::vector<metro_viewer::UnderlineInfo> underlines = underlines_; |
| 527 // Adjusts the offset. |
| 528 for (size_t i = 0; i < underlines_.size(); ++i) { |
| 529 underlines[i].start_offset -= new_committed_size; |
| 530 underlines[i].end_offset -= new_committed_size; |
| 531 } |
| 532 int32 selection_start = 0; |
| 533 int32 selection_end = 0; |
| 534 if (selection_start_ >= new_committed_size) |
| 535 selection_start = selection_start_ - new_committed_size; |
| 536 if (selection_end_ >= new_committed_size) |
| 537 selection_end = selection_end_ - new_committed_size; |
| 538 delegate_->OnCompositionChanged( |
| 539 composition_string, selection_start, selection_end, underlines); |
535 } | 540 } |
536 | 541 |
537 // Calls TextInputClient::SetCompositionText(). | |
538 CompositionText composition_text; | |
539 composition_text.text = composition_string; | |
540 composition_text.underlines = composition_undelines_; | |
541 // Adjusts the offset. | |
542 for (size_t i = 0; i < composition_text.underlines.size(); ++i) { | |
543 composition_text.underlines[i].start_offset -= new_committed_size; | |
544 composition_text.underlines[i].end_offset -= new_committed_size; | |
545 } | |
546 if (selection_.start() < new_committed_size) { | |
547 composition_text.selection.set_start(0); | |
548 } else { | |
549 composition_text.selection.set_start( | |
550 selection_.start() - new_committed_size); | |
551 } | |
552 if (selection_.end() < new_committed_size) { | |
553 composition_text.selection.set_end(0); | |
554 } else { | |
555 composition_text.selection.set_end(selection_.end() - new_committed_size); | |
556 } | |
557 if (text_input_client_) | |
558 text_input_client_->SetCompositionText(composition_text); | |
559 | |
560 // If there is no composition string, clear the text store status. | 542 // If there is no composition string, clear the text store status. |
561 // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange(). | 543 // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange(). |
562 if ((composition_string.empty()) && (new_committed_size != 0)) { | 544 if ((composition_string.empty()) && (new_committed_size != 0)) { |
563 string_buffer_.clear(); | 545 string_buffer_.clear(); |
564 committed_size_ = 0; | 546 committed_size_ = 0; |
565 selection_.set_start(0); | 547 selection_start_ = 0; |
566 selection_.set_end(0); | 548 selection_end_ = 0; |
567 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) | 549 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) |
568 text_store_acp_sink_->OnSelectionChange(); | 550 text_store_acp_sink_->OnSelectionChange(); |
569 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) | 551 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) |
570 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); | 552 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); |
571 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { | 553 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { |
572 TS_TEXTCHANGE textChange; | 554 TS_TEXTCHANGE textChange = {}; |
573 textChange.acpStart = 0; | 555 textChange.acpStart = 0; |
574 textChange.acpOldEnd = new_committed_size; | 556 textChange.acpOldEnd = new_committed_size; |
575 textChange.acpNewEnd = 0; | 557 textChange.acpNewEnd = 0; |
576 text_store_acp_sink_->OnTextChange(0, &textChange); | 558 text_store_acp_sink_->OnTextChange(0, &textChange); |
577 } | 559 } |
578 } | 560 } |
579 | 561 |
580 return S_OK; | 562 return S_OK; |
581 } | 563 } |
582 | 564 |
583 STDMETHODIMP TSFTextStore::RequestSupportedAttrs( | 565 STDMETHODIMP TextStore::RequestSupportedAttrs( |
584 DWORD /* flags */, // Seems that we should ignore this. | 566 DWORD /* flags */, // Seems that we should ignore this. |
585 ULONG attribute_buffer_size, | 567 ULONG attribute_buffer_size, |
586 const TS_ATTRID* attribute_buffer) { | 568 const TS_ATTRID* attribute_buffer) { |
587 if (!attribute_buffer) | 569 if (!attribute_buffer) |
588 return E_INVALIDARG; | 570 return E_INVALIDARG; |
589 if (!text_input_client_) | 571 if (!input_scope_) |
590 return E_FAIL; | 572 return E_FAIL; |
591 // We support only input scope attribute. | 573 // We support only input scope attribute. |
592 for (size_t i = 0; i < attribute_buffer_size; ++i) { | 574 for (size_t i = 0; i < attribute_buffer_size; ++i) { |
593 if (IsEqualGUID(GUID_PROP_INPUTSCOPE, attribute_buffer[i])) | 575 if (IsEqualGUID(GUID_PROP_INPUTSCOPE, attribute_buffer[i])) |
594 return S_OK; | 576 return S_OK; |
595 } | 577 } |
596 return E_FAIL; | 578 return E_FAIL; |
597 } | 579 } |
598 | 580 |
599 STDMETHODIMP TSFTextStore::RetrieveRequestedAttrs( | 581 STDMETHODIMP TextStore::RetrieveRequestedAttrs( |
600 ULONG attribute_buffer_size, | 582 ULONG attribute_buffer_size, |
601 TS_ATTRVAL* attribute_buffer, | 583 TS_ATTRVAL* attribute_buffer, |
602 ULONG* attribute_buffer_copied) { | 584 ULONG* attribute_buffer_copied) { |
603 if (!attribute_buffer_copied) | 585 if (!attribute_buffer_copied) |
604 return E_INVALIDARG; | 586 return E_INVALIDARG; |
| 587 *attribute_buffer_copied = 0; |
605 if (!attribute_buffer) | 588 if (!attribute_buffer) |
606 return E_INVALIDARG; | 589 return E_INVALIDARG; |
607 if (!text_input_client_) | 590 if (!input_scope_) |
608 return E_UNEXPECTED; | 591 return E_FAIL; |
609 // We support only input scope attribute. | 592 // We support only input scope attribute. |
610 *attribute_buffer_copied = 0; | |
611 if (attribute_buffer_size == 0) | 593 if (attribute_buffer_size == 0) |
612 return S_OK; | 594 return S_OK; |
613 | 595 |
614 attribute_buffer[0].dwOverlapId = 0; | 596 attribute_buffer[0].dwOverlapId = 0; |
615 attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE; | 597 attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE; |
616 attribute_buffer[0].varValue.vt = VT_UNKNOWN; | 598 attribute_buffer[0].varValue.vt = VT_UNKNOWN; |
617 attribute_buffer[0].varValue.punkVal = tsf_inputscope::CreateInputScope( | 599 attribute_buffer[0].varValue.punkVal = input_scope_; |
618 text_input_client_->GetTextInputType(), | |
619 text_input_client_->GetTextInputMode()); | |
620 attribute_buffer[0].varValue.punkVal->AddRef(); | 600 attribute_buffer[0].varValue.punkVal->AddRef(); |
621 *attribute_buffer_copied = 1; | 601 *attribute_buffer_copied = 1; |
| 602 |
622 return S_OK; | 603 return S_OK; |
623 } | 604 } |
624 | 605 |
625 STDMETHODIMP TSFTextStore::SetSelection( | 606 STDMETHODIMP TextStore::SetSelection( |
626 ULONG selection_buffer_size, | 607 ULONG selection_buffer_size, |
627 const TS_SELECTION_ACP* selection_buffer) { | 608 const TS_SELECTION_ACP* selection_buffer) { |
628 if (!HasReadWriteLock()) | 609 if (!HasReadWriteLock()) |
629 return TF_E_NOLOCK; | 610 return TF_E_NOLOCK; |
630 if (selection_buffer_size > 0) { | 611 if (selection_buffer_size > 0) { |
631 const LONG start_pos = selection_buffer[0].acpStart; | 612 const LONG start_pos = selection_buffer[0].acpStart; |
632 const LONG end_pos = selection_buffer[0].acpEnd; | 613 const LONG end_pos = selection_buffer[0].acpEnd; |
633 if (!((static_cast<LONG>(committed_size_) <= start_pos) && | 614 if (!((static_cast<LONG>(committed_size_) <= start_pos) && |
634 (start_pos <= end_pos) && | 615 (start_pos <= end_pos) && |
635 (end_pos <= static_cast<LONG>(string_buffer_.size())))) { | 616 (end_pos <= static_cast<LONG>(string_buffer_.size())))) { |
636 return TF_E_INVALIDPOS; | 617 return TF_E_INVALIDPOS; |
637 } | 618 } |
638 selection_.set_start(start_pos); | 619 selection_start_ = start_pos; |
639 selection_.set_end(end_pos); | 620 selection_end_ = end_pos; |
640 } | 621 } |
641 return S_OK; | 622 return S_OK; |
642 } | 623 } |
643 | 624 |
644 STDMETHODIMP TSFTextStore::SetText(DWORD flags, | 625 STDMETHODIMP TextStore::SetText(DWORD flags, |
645 LONG acp_start, | 626 LONG acp_start, |
646 LONG acp_end, | 627 LONG acp_end, |
647 const wchar_t* text_buffer, | 628 const wchar_t* text_buffer, |
648 ULONG text_buffer_size, | 629 ULONG text_buffer_size, |
649 TS_TEXTCHANGE* text_change) { | 630 TS_TEXTCHANGE* text_change) { |
650 if (!HasReadWriteLock()) | 631 if (!HasReadWriteLock()) |
651 return TS_E_NOLOCK; | 632 return TS_E_NOLOCK; |
652 if (!((static_cast<LONG>(committed_size_) <= acp_start) && | 633 if (!((static_cast<LONG>(committed_size_) <= acp_start) && |
653 (acp_start <= acp_end) && | 634 (acp_start <= acp_end) && |
654 (acp_end <= static_cast<LONG>(string_buffer_.size())))) { | 635 (acp_end <= static_cast<LONG>(string_buffer_.size())))) { |
655 return TS_E_INVALIDPOS; | 636 return TS_E_INVALIDPOS; |
656 } | 637 } |
657 | 638 |
658 TS_SELECTION_ACP selection; | 639 TS_SELECTION_ACP selection = {}; |
659 selection.acpStart = acp_start; | 640 selection.acpStart = acp_start; |
660 selection.acpEnd = acp_end; | 641 selection.acpEnd = acp_end; |
661 selection.style.ase = TS_AE_NONE; | 642 selection.style.ase = TS_AE_NONE; |
662 selection.style.fInterimChar = 0; | 643 selection.style.fInterimChar = 0; |
663 | 644 |
664 HRESULT ret; | 645 HRESULT ret = SetSelection(1, &selection); |
665 ret = SetSelection(1, &selection); | |
666 if (ret != S_OK) | 646 if (ret != S_OK) |
667 return ret; | 647 return ret; |
668 | 648 |
669 TS_TEXTCHANGE change; | 649 TS_TEXTCHANGE change; |
670 ret = InsertTextAtSelection(0, text_buffer, text_buffer_size, | 650 ret = InsertTextAtSelection( |
671 &acp_start, &acp_end, &change); | 651 0, text_buffer, text_buffer_size, &acp_start, &acp_end, &change); |
672 if (ret != S_OK) | 652 if (ret != S_OK) |
673 return ret; | 653 return ret; |
674 | 654 |
675 if (text_change) | 655 if (text_change) |
676 *text_change = change; | 656 *text_change = change; |
677 | 657 |
678 return S_OK; | 658 return S_OK; |
679 } | 659 } |
680 | 660 |
681 STDMETHODIMP TSFTextStore::UnadviseSink(IUnknown* unknown) { | 661 STDMETHODIMP TextStore::UnadviseSink(IUnknown* unknown) { |
682 if (!text_store_acp_sink_.IsSameObject(unknown)) | 662 if (!text_store_acp_sink_.IsSameObject(unknown)) |
683 return CONNECT_E_NOCONNECTION; | 663 return CONNECT_E_NOCONNECTION; |
684 text_store_acp_sink_.Release(); | 664 text_store_acp_sink_.Release(); |
685 text_store_acp_sink_mask_ = 0; | 665 text_store_acp_sink_mask_ = 0; |
686 return S_OK; | 666 return S_OK; |
687 } | 667 } |
688 | 668 |
689 STDMETHODIMP TSFTextStore::OnStartComposition( | 669 STDMETHODIMP TextStore::OnStartComposition( |
690 ITfCompositionView* composition_view, | 670 ITfCompositionView* composition_view, |
691 BOOL* ok) { | 671 BOOL* ok) { |
692 if (ok) | 672 if (ok) |
693 *ok = TRUE; | 673 *ok = TRUE; |
694 return S_OK; | 674 return S_OK; |
695 } | 675 } |
696 | 676 |
697 STDMETHODIMP TSFTextStore::OnUpdateComposition( | 677 STDMETHODIMP TextStore::OnUpdateComposition( |
698 ITfCompositionView* composition_view, | 678 ITfCompositionView* composition_view, |
699 ITfRange* range) { | 679 ITfRange* range) { |
700 return S_OK; | 680 return S_OK; |
701 } | 681 } |
702 | 682 |
703 STDMETHODIMP TSFTextStore::OnEndComposition( | 683 STDMETHODIMP TextStore::OnEndComposition( |
704 ITfCompositionView* composition_view) { | 684 ITfCompositionView* composition_view) { |
705 return S_OK; | 685 return S_OK; |
706 } | 686 } |
707 | 687 |
708 STDMETHODIMP TSFTextStore::OnEndEdit(ITfContext* context, | 688 STDMETHODIMP TextStore::OnEndEdit(ITfContext* context, |
709 TfEditCookie read_only_edit_cookie, | 689 TfEditCookie read_only_edit_cookie, |
710 ITfEditRecord* edit_record) { | 690 ITfEditRecord* edit_record) { |
711 if (!context || !edit_record) | 691 if (!context || !edit_record) |
712 return E_INVALIDARG; | 692 return E_INVALIDARG; |
713 | 693 if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size_, |
714 size_t committed_size; | 694 &underlines_)) { |
715 CompositionUnderlines undelines; | 695 // TODO(yukawa): Error handling. |
716 if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size, | |
717 &undelines)) { | |
718 return S_OK; | 696 return S_OK; |
719 } | 697 } |
720 composition_undelines_ = undelines; | |
721 committed_size_ = committed_size; | |
722 edit_flag_ = true; | 698 edit_flag_ = true; |
723 return S_OK; | 699 return S_OK; |
724 } | 700 } |
725 | 701 |
726 bool TSFTextStore::GetDisplayAttribute(TfGuidAtom guid_atom, | 702 bool TextStore::GetDisplayAttribute(TfGuidAtom guid_atom, |
727 TF_DISPLAYATTRIBUTE* attribute) { | 703 TF_DISPLAYATTRIBUTE* attribute) { |
728 GUID guid; | 704 GUID guid; |
729 if (FAILED(category_manager_->GetGUID(guid_atom, &guid))) | 705 if (FAILED(category_manager_->GetGUID(guid_atom, &guid))) |
730 return false; | 706 return false; |
731 | 707 |
732 base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info; | 708 base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info; |
733 if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo( | 709 if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo( |
734 guid, display_attribute_info.Receive(), NULL))) { | 710 guid, display_attribute_info.Receive(), NULL))) { |
735 return false; | 711 return false; |
736 } | 712 } |
737 return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute)); | 713 return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute)); |
738 } | 714 } |
739 | 715 |
740 bool TSFTextStore::GetCompositionStatus( | 716 bool TextStore::GetCompositionStatus( |
741 ITfContext* context, | 717 ITfContext* context, |
742 const TfEditCookie read_only_edit_cookie, | 718 const TfEditCookie read_only_edit_cookie, |
743 size_t* committed_size, | 719 size_t* committed_size, |
744 CompositionUnderlines* undelines) { | 720 std::vector<metro_viewer::UnderlineInfo>* undelines) { |
745 DCHECK(context); | 721 DCHECK(context); |
746 DCHECK(committed_size); | 722 DCHECK(committed_size); |
747 DCHECK(undelines); | 723 DCHECK(undelines); |
748 const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE}; | 724 const GUID* kTargetGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE}; |
749 base::win::ScopedComPtr<ITfReadOnlyProperty> track_property; | 725 base::win::ScopedComPtr<ITfReadOnlyProperty> track_property; |
750 if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0, | 726 if (FAILED(context->TrackProperties(kTargetGuids, 2, NULL, 0, |
751 track_property.Receive()))) { | 727 track_property.Receive()))) { |
752 return false; | 728 return false; |
753 } | 729 } |
754 | 730 |
755 *committed_size = 0; | 731 *committed_size = 0; |
756 undelines->clear(); | 732 undelines->clear(); |
757 base::win::ScopedComPtr<ITfRange> start_to_end_range; | 733 base::win::ScopedComPtr<ITfRange> start_to_end_range; |
758 base::win::ScopedComPtr<ITfRange> end_range; | 734 base::win::ScopedComPtr<ITfRange> end_range; |
759 if (FAILED(context->GetStart(read_only_edit_cookie, | 735 if (FAILED(context->GetStart(read_only_edit_cookie, |
760 start_to_end_range.Receive()))) { | 736 start_to_end_range.Receive()))) { |
(...skipping 18 matching lines...) Expand all Loading... |
779 break; | 755 break; |
780 base::win::ScopedVariant value; | 756 base::win::ScopedVariant value; |
781 base::win::ScopedComPtr<IEnumTfPropertyValue> enum_prop_value; | 757 base::win::ScopedComPtr<IEnumTfPropertyValue> enum_prop_value; |
782 if (FAILED(track_property->GetValue(read_only_edit_cookie, range, | 758 if (FAILED(track_property->GetValue(read_only_edit_cookie, range, |
783 value.Receive()))) { | 759 value.Receive()))) { |
784 return false; | 760 return false; |
785 } | 761 } |
786 if (FAILED(enum_prop_value.QueryFrom(value.AsInput()->punkVal))) | 762 if (FAILED(enum_prop_value.QueryFrom(value.AsInput()->punkVal))) |
787 return false; | 763 return false; |
788 | 764 |
789 TF_PROPERTYVAL property_value; | 765 TF_PROPERTYVAL property_value = {}; |
790 bool is_composition = false; | 766 bool is_composition = false; |
791 bool has_display_attribute = false; | 767 bool has_display_attribute = false; |
792 TF_DISPLAYATTRIBUTE display_attribute; | 768 TF_DISPLAYATTRIBUTE display_attribute; |
793 while (enum_prop_value->Next(1, &property_value, NULL) == S_OK) { | 769 while (enum_prop_value->Next(1, &property_value, NULL) == S_OK) { |
794 if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) { | 770 if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) { |
795 is_composition = (property_value.varValue.lVal == TRUE); | 771 is_composition = (property_value.varValue.lVal == TRUE); |
796 } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) { | 772 } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) { |
797 TfGuidAtom guid_atom = | 773 TfGuidAtom guid_atom = |
798 static_cast<TfGuidAtom>(property_value.varValue.lVal); | 774 static_cast<TfGuidAtom>(property_value.varValue.lVal); |
799 if (GetDisplayAttribute(guid_atom, &display_attribute)) | 775 if (GetDisplayAttribute(guid_atom, &display_attribute)) |
800 has_display_attribute = true; | 776 has_display_attribute = true; |
801 } | 777 } |
802 VariantClear(&property_value.varValue); | 778 VariantClear(&property_value.varValue); |
803 } | 779 } |
804 | 780 |
805 base::win::ScopedComPtr<ITfRangeACP> range_acp; | 781 base::win::ScopedComPtr<ITfRangeACP> range_acp; |
806 range_acp.QueryFrom(range); | 782 range_acp.QueryFrom(range); |
807 LONG start_pos, length; | 783 LONG start_pos, length; |
808 range_acp->GetExtent(&start_pos, &length); | 784 range_acp->GetExtent(&start_pos, &length); |
809 if (!is_composition) { | 785 if (!is_composition) { |
810 if (*committed_size < static_cast<size_t>(start_pos + length)) | 786 if (*committed_size < static_cast<size_t>(start_pos + length)) |
811 *committed_size = start_pos + length; | 787 *committed_size = start_pos + length; |
812 } else { | 788 } else { |
813 CompositionUnderline underline; | 789 metro_viewer::UnderlineInfo underline; |
814 underline.start_offset = start_pos; | 790 underline.start_offset = start_pos; |
815 underline.end_offset = start_pos + length; | 791 underline.end_offset = start_pos + length; |
816 underline.color = SK_ColorBLACK; | 792 underline.thick = !!display_attribute.fBoldLine; |
817 if (has_display_attribute) | |
818 underline.thick = !!display_attribute.fBoldLine; | |
819 undelines->push_back(underline); | 793 undelines->push_back(underline); |
820 } | 794 } |
821 } | 795 } |
822 return true; | 796 return true; |
823 } | 797 } |
824 | 798 |
825 void TSFTextStore::SetFocusedTextInputClient( | 799 bool TextStore::CancelComposition() { |
826 HWND focused_window, | |
827 TextInputClient* text_input_client) { | |
828 window_handle_ = focused_window; | |
829 text_input_client_ = text_input_client; | |
830 } | |
831 | |
832 void TSFTextStore::RemoveFocusedTextInputClient( | |
833 TextInputClient* text_input_client) { | |
834 if (text_input_client_ == text_input_client) { | |
835 window_handle_ = NULL; | |
836 text_input_client_ = NULL; | |
837 } | |
838 } | |
839 | |
840 bool TSFTextStore::CancelComposition() { | |
841 // If there is an on-going document lock, we must not edit the text. | 800 // If there is an on-going document lock, we must not edit the text. |
842 if (edit_flag_) | 801 if (edit_flag_) |
843 return false; | 802 return false; |
844 | 803 |
845 if (string_buffer_.empty()) | 804 if (string_buffer_.empty()) |
846 return true; | 805 return true; |
847 | 806 |
848 // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does | 807 // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does |
849 // not have a dedicated method to cancel composition. However, CUAS actually | 808 // not have a dedicated method to cancel composition. However, CUAS actually |
850 // has a protocol conversion from CPS_CANCEL into TSF operations. According | 809 // has a protocol conversion from CPS_CANCEL into TSF operations. According |
851 // to the observations on Windows 7, TIPs are expected to cancel composition | 810 // to the observations on Windows 7, TIPs are expected to cancel composition |
852 // when an on-going composition text is replaced with an empty string. So | 811 // when an on-going composition text is replaced with an empty string. So |
853 // we use the same operation to cancel composition here to minimize the risk | 812 // we use the same operation to cancel composition here to minimize the risk |
854 // of potential compatibility issues. | 813 // of potential compatibility issues. |
855 | 814 |
856 const size_t previous_buffer_size = string_buffer_.size(); | 815 const size_t previous_buffer_size = string_buffer_.size(); |
857 string_buffer_.clear(); | 816 string_buffer_.clear(); |
858 committed_size_ = 0; | 817 committed_size_ = 0; |
859 selection_.set_start(0); | 818 selection_start_ = 0; |
860 selection_.set_end(0); | 819 selection_end_ = 0; |
861 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) | 820 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) |
862 text_store_acp_sink_->OnSelectionChange(); | 821 text_store_acp_sink_->OnSelectionChange(); |
863 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) | 822 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) |
864 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); | 823 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); |
865 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { | 824 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { |
866 TS_TEXTCHANGE textChange = {}; | 825 TS_TEXTCHANGE textChange = {}; |
867 textChange.acpStart = 0; | 826 textChange.acpStart = 0; |
868 textChange.acpOldEnd = previous_buffer_size; | 827 textChange.acpOldEnd = previous_buffer_size; |
869 textChange.acpNewEnd = 0; | 828 textChange.acpNewEnd = 0; |
870 text_store_acp_sink_->OnTextChange(0, &textChange); | 829 text_store_acp_sink_->OnTextChange(0, &textChange); |
871 } | 830 } |
872 return true; | 831 return true; |
873 } | 832 } |
874 | 833 |
875 bool TSFTextStore::ConfirmComposition() { | 834 bool TextStore::ConfirmComposition() { |
876 // If there is an on-going document lock, we must not edit the text. | 835 // If there is an on-going document lock, we must not edit the text. |
877 if (edit_flag_) | 836 if (edit_flag_) |
878 return false; | 837 return false; |
879 | 838 |
880 if (string_buffer_.empty()) | 839 if (string_buffer_.empty()) |
881 return true; | 840 return true; |
882 | 841 |
883 // See the comment in TSFTextStore::CancelComposition. | 842 // See the comment in TextStore::CancelComposition. |
884 // This logic is based on the observation about how to emulate | 843 // This logic is based on the observation about how to emulate |
885 // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS. | 844 // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS. |
886 | 845 |
887 const string16& composition_text = string_buffer_.substr(committed_size_); | 846 const string16& composition_text = string_buffer_.substr(committed_size_); |
888 if (!composition_text.empty()) | 847 if (!composition_text.empty()) |
889 text_input_client_->InsertText(composition_text); | 848 delegate_->OnTextCommitted(composition_text); |
890 | 849 |
891 const size_t previous_buffer_size = string_buffer_.size(); | 850 const size_t previous_buffer_size = string_buffer_.size(); |
892 string_buffer_.clear(); | 851 string_buffer_.clear(); |
893 committed_size_ = 0; | 852 committed_size_ = 0; |
894 selection_.set_start(0); | 853 selection_start_ = 0; |
895 selection_.set_end(0); | 854 selection_end_ = 0; |
896 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) | 855 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) |
897 text_store_acp_sink_->OnSelectionChange(); | 856 text_store_acp_sink_->OnSelectionChange(); |
898 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) | 857 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) |
899 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); | 858 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); |
900 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { | 859 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { |
901 TS_TEXTCHANGE textChange = {}; | 860 TS_TEXTCHANGE textChange = {}; |
902 textChange.acpStart = 0; | 861 textChange.acpStart = 0; |
903 textChange.acpOldEnd = previous_buffer_size; | 862 textChange.acpOldEnd = previous_buffer_size; |
904 textChange.acpNewEnd = 0; | 863 textChange.acpNewEnd = 0; |
905 text_store_acp_sink_->OnTextChange(0, &textChange); | 864 text_store_acp_sink_->OnTextChange(0, &textChange); |
906 } | 865 } |
907 return true; | 866 return true; |
908 } | 867 } |
909 | 868 |
910 void TSFTextStore::SendOnLayoutChange() { | 869 void TextStore::SendOnLayoutChange() { |
911 if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)) | 870 if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)) |
912 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); | 871 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); |
913 } | 872 } |
914 | 873 |
915 bool TSFTextStore::HasReadLock() const { | 874 bool TextStore::HasReadLock() const { |
916 return (current_lock_type_ & TS_LF_READ) == TS_LF_READ; | 875 return (current_lock_type_ & TS_LF_READ) == TS_LF_READ; |
917 } | 876 } |
918 | 877 |
919 bool TSFTextStore::HasReadWriteLock() const { | 878 bool TextStore::HasReadWriteLock() const { |
920 return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE; | 879 return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE; |
921 } | 880 } |
922 | 881 |
923 } // namespace ui | 882 } // namespace metro_driver |
OLD | NEW |