blob: 52941940e2810a7f68a7232b041a1320c86de7d5 [file] [log] [blame]
// 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