| // 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_element_manager.h" |
| |
| #define _ATL_NO_AUTOMATIC_NAMESPACE |
| #define _WTL_NO_AUTOMATIC_NAMESPACE |
| #include <atlbase.h> |
| #include <atlcom.h> |
| #include <atlstr.h> |
| #include <msctf.h> |
| |
| #include <unordered_map> |
| |
| #include "renderer/renderer_command.pb.h" |
| #include "session/commands.pb.h" |
| #include "win32/base/input_state.h" |
| #include "win32/tip/tip_input_mode_manager.h" |
| #include "win32/tip/tip_private_context.h" |
| #include "win32/tip/tip_text_service.h" |
| #include "win32/tip/tip_thread_context.h" |
| #include "win32/tip/tip_ui_handler.h" |
| |
| namespace mozc { |
| namespace win32 { |
| namespace tsf { |
| |
| namespace { |
| |
| using ATL::CComPtr; |
| using ATL::CComQIPtr; |
| using ::mozc::commands::Output; |
| typedef ::mozc::commands::RendererCommand_IndicatorInfo IndicatorInfo; |
| |
| struct UIElementInfo { |
| UIElementInfo() |
| : id(TF_INVALID_UIELEMENTID) { |
| } |
| DWORD id; |
| CComPtr<ITfUIElement> element; |
| }; |
| |
| HRESULT BeginUI(ITfUIElementMgr *ui_element_manager, |
| ITfUIElement *ui_element, |
| DWORD *new_element_id) { |
| BOOL show = FALSE; |
| *new_element_id = TF_INVALID_UIELEMENTID; |
| CComPtr<ITfUIElement> element(ui_element); |
| const HRESULT result = ui_element_manager->BeginUIElement( |
| element, &show, new_element_id); |
| if (FAILED(result)) { |
| return result; |
| } |
| element->Show(show); |
| return S_OK; |
| } |
| |
| HRESULT EndUI(ITfUIElementMgr *ui_element_manager, DWORD element_id) { |
| CComPtr<ITfUIElement> element; |
| ui_element_manager->GetUIElement(element_id, &element); |
| if (element) { |
| element->Show(FALSE); |
| } |
| ui_element_manager->EndUIElement(element_id); |
| return S_OK; |
| } |
| |
| } // namespace |
| |
| class TipUiElementManager::UiElementMap |
| : public std::unordered_map<TipUiElementManager::UIElementFlags, |
| UIElementInfo> { |
| }; |
| |
| TipUiElementManager::TipUiElementManager() |
| : ui_element_map_(new UiElementMap) {} |
| |
| TipUiElementManager::~TipUiElementManager() {} |
| |
| ITfUIElement *TipUiElementManager::GetElement(UIElementFlags element) const { |
| const UiElementMap::const_iterator it = ui_element_map_->find(element); |
| if (it == ui_element_map_->end()) { |
| return nullptr; |
| } |
| return it->second.element; |
| } |
| |
| DWORD TipUiElementManager::GetElementId(UIElementFlags element) const { |
| const UiElementMap::const_iterator it = ui_element_map_->find(element); |
| if (it == ui_element_map_->end()) { |
| return TF_INVALID_UIELEMENTID; |
| } |
| return it->second.id; |
| } |
| |
| HRESULT TipUiElementManager::OnUpdate( |
| TipTextService *text_service, ITfContext *context) { |
| CComQIPtr<ITfUIElementMgr> ui_element_manager = |
| text_service->GetThreadManager(); |
| if (!ui_element_manager) { |
| return E_FAIL; |
| } |
| TipPrivateContext *private_context = |
| text_service->GetPrivateContext(context); |
| if (private_context == nullptr) { |
| return E_FAIL; |
| } |
| |
| const Output &output = private_context->last_output(); |
| |
| uint32 existence_bits = kNoneWindow; |
| if (output.has_candidates() && output.candidates().has_category()) { |
| switch (output.candidates().category()) { |
| case commands::SUGGESTION: |
| existence_bits |= kSuggestWindow; |
| break; |
| case commands::PREDICTION: |
| case commands::CONVERSION: |
| existence_bits |= kCandidateWindow; |
| break; |
| default: |
| break; |
| } |
| } |
| if (private_context->input_behavior().use_mode_indicator && |
| text_service->GetThreadContext()->GetInputModeManager()-> |
| IsIndicatorVisible()) { |
| existence_bits |= kIndicatorWindow; |
| } |
| |
| DWORD suggest_ui_id = TF_INVALID_UIELEMENTID; |
| CComPtr<ITfUIElement> suggest_ui; |
| { |
| const UiElementMap::const_iterator it = |
| ui_element_map_->find(kSuggestWindow); |
| if (it != ui_element_map_->end()) { |
| suggest_ui_id = it->second.id; |
| suggest_ui = it->second.element; |
| } |
| } |
| DWORD candidate_ui_id = TF_INVALID_UIELEMENTID; |
| CComPtr<ITfUIElement> candidate_ui; |
| { |
| const UiElementMap::const_iterator it = |
| ui_element_map_->find(kCandidateWindow); |
| if (it != ui_element_map_->end()) { |
| candidate_ui_id = it->second.id; |
| candidate_ui = it->second.element; |
| } |
| } |
| DWORD indicator_ui_id = TF_INVALID_UIELEMENTID; |
| CComPtr<ITfUIElement> indicator_ui; |
| { |
| const UiElementMap::const_iterator it = |
| ui_element_map_->find(kIndicatorWindow); |
| if (it != ui_element_map_->end()) { |
| indicator_ui_id = it->second.id; |
| indicator_ui = it->second.element; |
| } |
| } |
| |
| enum UpdateMode { |
| kUINone, // UI is not changed. |
| kUIBeginAndUpdate, // Begin() and Update() should be called. |
| kUIEnd, // End() should be called. |
| kUIUpdate, // Update() should be called. |
| }; |
| |
| UpdateMode suggest_mode = kUINone; |
| if ((existence_bits & kSuggestWindow) == kSuggestWindow) { |
| if (suggest_ui_id == TF_INVALID_UIELEMENTID) { |
| suggest_mode = kUIBeginAndUpdate; |
| } else { |
| suggest_mode = kUIUpdate; |
| } |
| } else { |
| if (suggest_ui_id != TF_INVALID_UIELEMENTID) { |
| suggest_mode = kUIEnd; |
| } |
| } |
| |
| UpdateMode candidate_mode = kUINone; |
| if ((existence_bits & kCandidateWindow) == kCandidateWindow) { |
| if (candidate_ui_id == TF_INVALID_UIELEMENTID) { |
| candidate_mode = kUIBeginAndUpdate; |
| } else { |
| candidate_mode = kUIUpdate; |
| } |
| } else { |
| if (candidate_ui_id != TF_INVALID_UIELEMENTID) { |
| candidate_mode = kUIEnd; |
| } |
| } |
| |
| UpdateMode indicator_mode = kUINone; |
| if ((existence_bits & kIndicatorWindow) == kIndicatorWindow) { |
| if (indicator_ui_id == TF_INVALID_UIELEMENTID) { |
| indicator_mode = kUIBeginAndUpdate; |
| } else { |
| indicator_mode = kUIUpdate; |
| } |
| } else { |
| if (indicator_ui_id != TF_INVALID_UIELEMENTID) { |
| indicator_mode = kUIEnd; |
| } |
| } |
| |
| if (suggest_mode == kUIEnd) { |
| EndUI(ui_element_manager, suggest_ui_id); |
| suggest_ui_id = TF_INVALID_UIELEMENTID; |
| ui_element_map_->erase(kSuggestWindow); |
| if (suggest_ui) { |
| TipUiHandler::OnDestroyElement(text_service, suggest_ui); |
| } |
| } |
| if (candidate_mode == kUIEnd) { |
| EndUI(ui_element_manager, candidate_ui_id); |
| candidate_ui_id = TF_INVALID_UIELEMENTID; |
| ui_element_map_->erase(kCandidateWindow); |
| if (candidate_ui) { |
| TipUiHandler::OnDestroyElement(text_service, candidate_ui); |
| } |
| } |
| if (indicator_mode == kUIEnd) { |
| EndUI(ui_element_manager, indicator_ui_id); |
| indicator_ui_id = TF_INVALID_UIELEMENTID; |
| ui_element_map_->erase(kIndicatorWindow); |
| if (indicator_ui) { |
| TipUiHandler::OnDestroyElement(text_service, indicator_ui); |
| } |
| } |
| |
| if (suggest_mode == kUIBeginAndUpdate) { |
| CComPtr<ITfUIElement> suggest_ui = TipUiHandler::CreateUI( |
| TipUiHandler::kSuggestWindow, text_service, context); |
| if (suggest_ui) { |
| DWORD new_suggest_ui_id = TF_INVALID_UIELEMENTID; |
| if (SUCCEEDED(BeginUI(ui_element_manager, |
| suggest_ui, |
| &new_suggest_ui_id))) { |
| (*ui_element_map_)[kSuggestWindow].element = suggest_ui; |
| (*ui_element_map_)[kSuggestWindow].id = new_suggest_ui_id; |
| suggest_ui_id = new_suggest_ui_id; |
| } |
| } |
| } |
| if (candidate_mode == kUIBeginAndUpdate) { |
| CComPtr<ITfUIElement> candidate_ui = TipUiHandler::CreateUI( |
| TipUiHandler::kCandidateWindow, text_service, context); |
| if (candidate_ui) { |
| DWORD new_candidate_ui_id = TF_INVALID_UIELEMENTID; |
| if (SUCCEEDED(BeginUI(ui_element_manager, |
| candidate_ui, |
| &new_candidate_ui_id))) { |
| (*ui_element_map_)[kCandidateWindow].element = candidate_ui; |
| (*ui_element_map_)[kCandidateWindow].id = new_candidate_ui_id; |
| candidate_ui_id = new_candidate_ui_id; |
| } |
| } |
| } |
| if (indicator_mode == kUIBeginAndUpdate) { |
| CComPtr<ITfUIElement> indicator_ui = TipUiHandler::CreateUI( |
| TipUiHandler::kIndicatorWindow, text_service, context); |
| if (indicator_ui) { |
| DWORD new_indicator_ui_id = TF_INVALID_UIELEMENTID; |
| if (SUCCEEDED(BeginUI(ui_element_manager, |
| indicator_ui, |
| &new_indicator_ui_id))) { |
| (*ui_element_map_)[kIndicatorWindow].element = indicator_ui; |
| (*ui_element_map_)[kIndicatorWindow].id = new_indicator_ui_id; |
| candidate_ui_id = new_indicator_ui_id; |
| } |
| } |
| } |
| |
| if (suggest_mode == kUIUpdate || suggest_mode == kUIBeginAndUpdate) { |
| ui_element_manager->UpdateUIElement(suggest_ui_id); |
| } |
| if (candidate_mode == kUIUpdate || candidate_mode == kUIBeginAndUpdate) { |
| ui_element_manager->UpdateUIElement(candidate_ui_id); |
| } |
| if (indicator_mode == kUIUpdate || indicator_mode == kUIBeginAndUpdate) { |
| ui_element_manager->UpdateUIElement(indicator_ui_id); |
| } |
| return S_OK; |
| } |
| |
| bool TipUiElementManager::IsVisible(ITfUIElementMgr *ui_element_manager, |
| UIElementFlags element) const { |
| if (ui_element_manager == nullptr) { |
| return false; |
| } |
| const UiElementMap::const_iterator it = ui_element_map_->find(element); |
| if (it == ui_element_map_->end()) { |
| return false; |
| } |
| |
| BOOL shown = FALSE; |
| if (FAILED(it->second.element->IsShown(&shown))) { |
| return false; |
| } |
| return !!shown; |
| } |
| |
| } // namespace tsf |
| } // namespace win32 |
| } // namespace mozc |