| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "win32/tip/tip_ui_handler_immersive.h" |
| |
| #define _ATL_NO_AUTOMATIC_NAMESPACE |
| #define _WTL_NO_AUTOMATIC_NAMESPACE |
| #include <atlbase.h> |
| #include <atlcom.h> |
| #include <atlapp.h> |
| #include <atlmisc.h> |
| #include <atlwin.h> |
| #include <msctf.h> |
| |
| #include <unordered_map> |
| |
| #include "base/util.h" |
| #include "session/commands.pb.h" |
| #include "win32/tip/tip_composition_util.h" |
| #include "win32/tip/tip_private_context.h" |
| #include "win32/tip/tip_text_service.h" |
| #include "win32/tip/tip_ui_element_immersive.h" |
| #include "win32/tip/tip_ui_element_manager.h" |
| |
| namespace mozc { |
| namespace win32 { |
| namespace tsf { |
| |
| namespace { |
| |
| using ATL::CComPtr; |
| using ATL::CComQIPtr; |
| using ATL::CWindow; |
| using WTL::CRect; |
| |
| using ::mozc::commands::Output; |
| using ::mozc::commands::Preedit; |
| typedef ::mozc::commands::Preedit_Segment Segment; |
| typedef ::mozc::commands::Preedit_Segment::Annotation Annotation; |
| |
| // Represents the module handle of this module. |
| volatile HMODULE g_module = nullptr; |
| |
| // True if the the DLL received DLL_PROCESS_DETACH notification. |
| volatile bool g_module_unloaded = false; |
| |
| // Thread Local Storage (TLS) index to specify the current UI thread is |
| // initialized or not. if ::GetTlsValue(g_tls_index) returns non-zero |
| // value, the current thread is initialized. |
| volatile DWORD g_tls_index = TLS_OUT_OF_INDEXES; |
| |
| using UiElementMap = std::unordered_map<ITfUIElement *, HWND>; |
| |
| class ThreadLocalInfo { |
| public: |
| ThreadLocalInfo() {} |
| UiElementMap *ui_element_map() { |
| return &ui_element_map_; |
| } |
| |
| private: |
| UiElementMap ui_element_map_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadLocalInfo); |
| }; |
| |
| ThreadLocalInfo *GetThreadLocalInfo() { |
| if (g_module_unloaded) { |
| return nullptr; |
| } |
| if (g_tls_index == TLS_OUT_OF_INDEXES) { |
| return nullptr; |
| } |
| ThreadLocalInfo *info = static_cast<ThreadLocalInfo *>( |
| ::TlsGetValue(g_tls_index)); |
| if (info != nullptr) { |
| // already initialized. |
| return info; |
| } |
| info = new ThreadLocalInfo(); |
| ::TlsSetValue(g_tls_index, info); |
| return info; |
| } |
| |
| void EnsureThreadLocalInfoDestroyed() { |
| if (g_module_unloaded) { |
| return; |
| } |
| if (g_tls_index == TLS_OUT_OF_INDEXES) { |
| return; |
| } |
| ThreadLocalInfo *info = static_cast<ThreadLocalInfo *>( |
| ::TlsGetValue(g_tls_index)); |
| if (info == nullptr) { |
| // already destroyes. |
| return; |
| } |
| delete info; |
| ::TlsSetValue(g_tls_index, nullptr); |
| } |
| |
| void UpdateUI(TipTextService *text_service, ITfContext *context) { |
| if (text_service == nullptr) { |
| return; |
| } |
| |
| ThreadLocalInfo *info = GetThreadLocalInfo(); |
| if (info == nullptr) { |
| return; |
| } |
| TipPrivateContext *private_context = |
| text_service->GetPrivateContext(context); |
| if (private_context == nullptr) { |
| return; |
| } |
| private_context->GetUiElementManager()->OnUpdate(text_service, context); |
| const UiElementMap &map = *info->ui_element_map(); |
| |
| const TipUiElementManager::UIElementFlags kUiFlags[] = { |
| TipUiElementManager::kSuggestWindow, |
| TipUiElementManager::kCandidateWindow, |
| }; |
| for (size_t i = 0; i < arraysize(kUiFlags); ++i) { |
| const CComPtr<ITfUIElement> ui_element = |
| private_context->GetUiElementManager()->GetElement(kUiFlags[i]); |
| if (ui_element) { |
| const UiElementMap::const_iterator it = map.find(ui_element); |
| if (it == map.end()) { |
| continue; |
| } |
| ::PostMessageW(it->second, WM_MOZC_IMMERSIVE_WINDOW_UPDATE, 0, 0); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| ITfUIElement *TipUiHandlerImmersive::CreateUI(TipUiHandler::UiType type, |
| TipTextService *text_service, |
| ITfContext *context) { |
| switch (type) { |
| case TipUiHandler::kSuggestWindow: |
| case TipUiHandler::kCandidateWindow: { |
| ThreadLocalInfo *info = GetThreadLocalInfo(); |
| if (info == nullptr) { |
| return nullptr; |
| } |
| HWND window_handle = nullptr; |
| CComPtr<ITfUIElement> element(TipUiElementImmersive::New( |
| text_service, context, &window_handle)); |
| if (element == nullptr) { |
| return nullptr; |
| } |
| if (window_handle == nullptr) { |
| return nullptr; |
| } |
| (*info->ui_element_map())[element] = window_handle; |
| // pass the ownership to the caller. |
| return element.Detach(); |
| } |
| default: |
| return nullptr; |
| } |
| } |
| |
| void TipUiHandlerImmersive::OnDestroyElement(ITfUIElement *element) { |
| ThreadLocalInfo *info = GetThreadLocalInfo(); |
| if (info == nullptr) { |
| return; |
| } |
| UiElementMap::iterator it = info->ui_element_map()->find(element); |
| if (it == info->ui_element_map()->end()) { |
| return; |
| } |
| ::DestroyWindow(it->second); |
| info->ui_element_map()->erase(it); |
| } |
| |
| void TipUiHandlerImmersive::OnActivate() { |
| TipUiElementImmersive::OnActivate(); |
| } |
| |
| void TipUiHandlerImmersive::OnDeactivate() { |
| EnsureThreadLocalInfoDestroyed(); |
| TipUiElementImmersive::OnDeactivate(); |
| } |
| |
| void TipUiHandlerImmersive::OnFocusChange( |
| TipTextService *text_service, ITfDocumentMgr *focused_document_manager) { |
| if (!focused_document_manager) { |
| // Empty document is not an error. |
| return; |
| } |
| |
| CComPtr<ITfContext> context; |
| if (FAILED(focused_document_manager->GetBase(&context))) { |
| return; |
| } |
| if (!context) { |
| return; |
| } |
| |
| UpdateUI(text_service, context); |
| } |
| |
| bool TipUiHandlerImmersive::Update(TipTextService *text_service, |
| ITfContext *context, |
| TfEditCookie read_cookie) { |
| UpdateUI(text_service, context); |
| return true; |
| } |
| |
| bool TipUiHandlerImmersive::OnDllProcessAttach(HINSTANCE module_handle, |
| bool static_loading) { |
| g_module = module_handle; |
| g_tls_index = ::TlsAlloc(); |
| return TipUiElementImmersive::OnDllProcessAttach( |
| module_handle, static_loading); |
| } |
| |
| void TipUiHandlerImmersive::OnDllProcessDetach(HINSTANCE module_handle, |
| bool process_shutdown) { |
| if (g_tls_index != TLS_OUT_OF_INDEXES) { |
| ::TlsFree(g_tls_index); |
| g_tls_index = TLS_OUT_OF_INDEXES; |
| } |
| g_module_unloaded = true; |
| g_module = nullptr; |
| TipUiElementImmersive::OnDllProcessDetach(module_handle, process_shutdown); |
| } |
| |
| } // namespace tsf |
| } // namespace win32 |
| } // namespace mozc |