OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "win8/metro_driver/ime/text_service.h" |
| 6 |
| 7 #include <inputscope.h> |
| 8 #include <msctf.h> |
| 9 |
| 10 #include "base/win/scoped_variant.h" |
| 11 #include "win8/metro_driver/ime/text_store.h" |
| 12 |
| 13 namespace metro_driver { |
| 14 namespace { |
| 15 |
| 16 class DocumentImpl { |
| 17 public: |
| 18 DocumentImpl() |
| 19 : edit_cookie_(TF_INVALID_COOKIE), |
| 20 text_edit_sink_cookie_(TF_INVALID_COOKIE) {} |
| 21 |
| 22 ~DocumentImpl() { |
| 23 Uninitialize(); |
| 24 } |
| 25 |
| 26 bool Initialize(ITfThreadMgr2* thread_manager, |
| 27 TfClientId client_id, |
| 28 TextStore* text_store) { |
| 29 Uninitialize(); |
| 30 |
| 31 base::win::ScopedComPtr<ITfDocumentMgr> document_manager; |
| 32 base::win::ScopedComPtr<ITfContext> context; |
| 33 |
| 34 if (FAILED(thread_manager->CreateDocumentMgr(document_manager.Receive()))) { |
| 35 return false; |
| 36 } |
| 37 |
| 38 DWORD edit_cookie = TF_INVALID_EDIT_COOKIE; |
| 39 if (FAILED(document_manager->CreateContext( |
| 40 client_id, |
| 41 0, |
| 42 static_cast<ITextStoreACP*>(text_store), |
| 43 context.Receive(), |
| 44 &edit_cookie))) { |
| 45 return false; |
| 46 } |
| 47 |
| 48 if (FAILED(document_manager->Push(context))) { |
| 49 return false; |
| 50 } |
| 51 |
| 52 base::win::ScopedComPtr<ITfSource> source; |
| 53 DWORD text_edit_sink_cookie = TF_INVALID_EDIT_COOKIE; |
| 54 if (text_store) { |
| 55 if (FAILED(source.QueryFrom(context))) { |
| 56 return false; |
| 57 } |
| 58 if (FAILED(source->AdviseSink(IID_ITfTextEditSink, |
| 59 static_cast<ITfTextEditSink*>(text_store), |
| 60 &text_edit_sink_cookie))) { |
| 61 return false; |
| 62 } |
| 63 } |
| 64 |
| 65 document_manager_ = document_manager; |
| 66 context_ = context; |
| 67 text_store_ = text_store; |
| 68 text_edit_sink_source_ = source; |
| 69 edit_cookie_ = edit_cookie; |
| 70 text_edit_sink_cookie_ = text_edit_sink_cookie; |
| 71 return true; |
| 72 } |
| 73 |
| 74 void Uninitialize() { |
| 75 if (text_edit_sink_source_ && |
| 76 (text_edit_sink_cookie_ != TF_INVALID_COOKIE)) { |
| 77 text_edit_sink_source_->UnadviseSink(text_edit_sink_cookie_); |
| 78 text_edit_sink_cookie_ = TF_INVALID_COOKIE; |
| 79 text_edit_sink_source_.Release(); |
| 80 } |
| 81 document_manager_.Release(); |
| 82 |
| 83 scoped_refptr<TextStore> text_store; |
| 84 text_store_.swap(text_store); |
| 85 } |
| 86 |
| 87 ITfDocumentMgr* document_manager() const { |
| 88 return document_manager_; |
| 89 } |
| 90 |
| 91 ITfContext* context() const { |
| 92 return context_; |
| 93 } |
| 94 |
| 95 scoped_refptr<TextStore> text_store() { |
| 96 return text_store_; |
| 97 } |
| 98 |
| 99 private: |
| 100 base::win::ScopedComPtr<ITfDocumentMgr> document_manager_; |
| 101 base::win::ScopedComPtr<ITfSource> text_edit_sink_source_; |
| 102 base::win::ScopedComPtr<ITfContext> context_; |
| 103 scoped_refptr<TextStore> text_store_; |
| 104 DWORD edit_cookie_; |
| 105 DWORD text_edit_sink_cookie_; |
| 106 |
| 107 DISALLOW_COPY_AND_ASSIGN(DocumentImpl); |
| 108 }; |
| 109 |
| 110 // Japanese IME expects the default value of this compartment is |
| 111 // TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value is |
| 112 // managed per thread, thus setting this value at once is enough. This |
| 113 // value never affects other IMEs except for Japanese. |
| 114 bool InitializeInputModeSentene(ITfThreadMgr2* thread_manager, |
| 115 TfClientId client_id) { |
| 116 base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager; |
| 117 if (FAILED(thread_compartment_manager.QueryFrom(thread_manager))) |
| 118 return false; |
| 119 |
| 120 base::win::ScopedComPtr<ITfCompartment> sentence_compartment; |
| 121 if (FAILED(thread_compartment_manager->GetCompartment( |
| 122 GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE, |
| 123 sentence_compartment.Receive()))) |
| 124 return false; |
| 125 |
| 126 base::win::ScopedVariant sentence_variant; |
| 127 sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT); |
| 128 if (FAILED(sentence_compartment->SetValue(client_id, &sentence_variant))) |
| 129 return false; |
| 130 return true; |
| 131 } |
| 132 |
| 133 // Initializes |context| where an IME will be disabled. |
| 134 bool InitializeDisabledContext(ITfContext* context, TfClientId client_id) { |
| 135 base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr; |
| 136 if (FAILED(compartment_mgr.QueryFrom(context))) |
| 137 return false; |
| 138 |
| 139 base::win::ScopedComPtr<ITfCompartment> disabled_compartment; |
| 140 if (FAILED(compartment_mgr->GetCompartment( |
| 141 GUID_COMPARTMENT_KEYBOARD_DISABLED, |
| 142 disabled_compartment.Receive()))) { |
| 143 return false; |
| 144 } |
| 145 |
| 146 base::win::ScopedVariant variant; |
| 147 variant.Set(1); |
| 148 if (FAILED(disabled_compartment->SetValue(client_id, &variant))) |
| 149 return false; |
| 150 |
| 151 base::win::ScopedComPtr<ITfCompartment> empty_context; |
| 152 if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT, |
| 153 empty_context.Receive()))) { |
| 154 return false; |
| 155 } |
| 156 |
| 157 base::win::ScopedVariant empty_context_variant; |
| 158 empty_context_variant.Set(static_cast<int32>(1)); |
| 159 if (FAILED(empty_context->SetValue(client_id, &empty_context_variant))) |
| 160 return false; |
| 161 |
| 162 return true; |
| 163 } |
| 164 |
| 165 } // namespace |
| 166 |
| 167 struct TextService::InternalState { |
| 168 InternalState() |
| 169 : client_id(TF_CLIENTID_NULL), |
| 170 win_event_hook_initialized(false), |
| 171 window_handle(NULL), |
| 172 text_store_delegate(NULL) {} |
| 173 |
| 174 TfClientId client_id; |
| 175 bool win_event_hook_initialized; |
| 176 scoped_ptr<DocumentImpl> current_document; |
| 177 base::win::ScopedComPtr<ITfThreadMgr2> thread_manager; |
| 178 HWND window_handle; |
| 179 TextStoreDelegate* text_store_delegate; |
| 180 }; |
| 181 |
| 182 TextService::TextService(HWND window_handle, |
| 183 TextStoreDelegate* text_store_delegate) |
| 184 : state_(new InternalState) { |
| 185 if (FAILED(state_->thread_manager.CreateInstance(CLSID_TF_ThreadMgr))) |
| 186 return; |
| 187 if (FAILED(state_->thread_manager->ActivateEx(&state_->client_id, 0))) |
| 188 return; |
| 189 state_->window_handle = window_handle; |
| 190 state_->text_store_delegate = text_store_delegate; |
| 191 InitializeInputModeSentene(state_->thread_manager, state_->client_id); |
| 192 } |
| 193 |
| 194 TextService::~TextService() { |
| 195 state_->current_document.reset(NULL); |
| 196 state_->thread_manager->Deactivate(); |
| 197 state_.reset(NULL); |
| 198 } |
| 199 |
| 200 void TextService::CancelComposition() { |
| 201 DocumentImpl* document = state_->current_document.get(); |
| 202 if (!document) |
| 203 return; |
| 204 TextStore* text_store = document->text_store(); |
| 205 if (!text_store) |
| 206 return; |
| 207 text_store->CancelComposition(); |
| 208 } |
| 209 |
| 210 void TextService::OnDocumentTypeChanged( |
| 211 const std::vector<int32>& input_scopes) { |
| 212 state_->current_document.reset(new DocumentImpl); |
| 213 TextStore* text_store = NULL; |
| 214 if (!input_scopes.empty()) { |
| 215 std::vector<InputScope> buffer(input_scopes.size()); |
| 216 for (size_t i = 0; i < input_scopes.size(); ++i) |
| 217 buffer[i] = static_cast<InputScope>(input_scopes[i]); |
| 218 text_store = new TextStore( |
| 219 state_->window_handle, buffer, state_->text_store_delegate); |
| 220 } |
| 221 scoped_ptr<DocumentImpl> new_document(new DocumentImpl); |
| 222 if (!new_document->Initialize(state_->thread_manager.get(), |
| 223 state_->client_id, |
| 224 text_store)) { |
| 225 return; |
| 226 } |
| 227 const bool has_password_field = |
| 228 std::find(input_scopes.begin(), input_scopes.end(), IS_PASSWORD) != |
| 229 input_scopes.end(); |
| 230 const bool use_disabled_context = input_scopes.empty() || has_password_field; |
| 231 if (use_disabled_context && |
| 232 !InitializeDisabledContext(new_document->context(), state_->client_id)) { |
| 233 return; |
| 234 } |
| 235 state_->current_document.swap(new_document); |
| 236 if (FAILED(state_->thread_manager->SetFocus( |
| 237 state_->current_document->document_manager()))) { |
| 238 return; |
| 239 } |
| 240 return; |
| 241 } |
| 242 |
| 243 void TextService::OnWindowActivated() { |
| 244 DocumentImpl* document = state_->current_document.get(); |
| 245 if (!document) |
| 246 return; |
| 247 if (FAILED(state_->thread_manager->SetFocus(document->document_manager()))) |
| 248 return; |
| 249 } |
| 250 |
| 251 } // namespace metro_driver |
| 252 |
OLD | NEW |